diff --git a/.gitignore b/.gitignore index b8512f3..bf800c2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ dist-ssr *.sw? .yarn/* +!.yarn/patches !.yarn/releases vite.config.ts.* *.module.css.d.ts diff --git a/contracts/amm/Nargo.toml b/contracts/amm/Nargo.toml index c7c7d0f..4471de7 100644 --- a/contracts/amm/Nargo.toml +++ b/contracts/amm/Nargo.toml @@ -4,6 +4,6 @@ authors = [""] type = "contract" [dependencies] -aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/aztec-nr/aztec" } -token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } -uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/aztec-nr/uint-note" } +aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/aztec-nr/aztec" } +token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } +uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/aztec-nr/uint-note" } diff --git a/contracts/proof_of_password/Nargo.toml b/contracts/proof_of_password/Nargo.toml index d215be2..a4f85a1 100644 --- a/contracts/proof_of_password/Nargo.toml +++ b/contracts/proof_of_password/Nargo.toml @@ -4,7 +4,7 @@ type = "contract" authors = [""] [dependencies] -aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/aztec-nr/aztec" } -token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } -poseidon = { tag = "v0.1.1", git = "https://github.com/noir-lang/poseidon" } -compressed_string = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.2.0-nightly.20260412", directory = "noir-projects/aztec-nr/compressed-string" } \ No newline at end of file +aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/aztec-nr/aztec" } +token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } +poseidon = { tag = "v0.3.0", git = "https://github.com/noir-lang/poseidon" } +compressed_string = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260417", directory = "noir-projects/aztec-nr/compressed-string" } \ No newline at end of file diff --git a/package.json b/package.json index 3159188..1757713 100644 --- a/package.json +++ b/package.json @@ -27,20 +27,20 @@ "local-aztec:status": "node scripts/toggle-local-aztec.js status" }, "dependencies": { - "@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/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", + "@aztec/accounts": "v4.3.0-nightly.20260417", + "@aztec/aztec.js": "v4.3.0-nightly.20260417", + "@aztec/constants": "v4.3.0-nightly.20260417", + "@aztec/entrypoints": "v4.3.0-nightly.20260417", + "@aztec/foundation": "v4.3.0-nightly.20260417", + "@aztec/noir-contracts.js": "v4.3.0-nightly.20260417", + "@aztec/protocol-contracts": "v4.3.0-nightly.20260417", + "@aztec/pxe": "v4.3.0-nightly.20260417", + "@aztec/stdlib": "v4.3.0-nightly.20260417", + "@aztec/wallet-sdk": "v4.3.0-nightly.20260417", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@gregojuice/contracts": "^0.0.15", - "@gregojuice/embedded-wallet": "^0.0.15", + "@gregojuice/contracts": "^0.0.18", + "@gregojuice/embedded-wallet": "^0.0.18", "@mui/icons-material": "^6.3.1", "@mui/material": "^6.3.1", "@mui/styles": "^6.3.1", @@ -48,10 +48,11 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-dropzone": "^14.3.5", - "zod": "^3.23.8" + "zod": "^3.23.8", + "zone.js": "^0.16.1" }, "devDependencies": { - "@aztec/wallets": "v4.2.0-nightly.20260412", + "@aztec/wallets": "v4.3.0-nightly.20260417", "@eslint/js": "^9.18.0", "@playwright/test": "1.49.0", "@types/buffer-json": "^2", diff --git a/scripts/update.js b/scripts/update.js index 5345723..809f905 100755 --- a/scripts/update.js +++ b/scripts/update.js @@ -7,20 +7,20 @@ * node scripts/update.js [--version VERSION] [--deploy [NETWORK]] [--skip-aztec-up] */ -import { readFileSync, writeFileSync } from "fs"; -import { resolve, dirname } from "path"; -import { fileURLToPath } from "url"; -import { execSync } from "child_process"; +import { readFileSync, writeFileSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { execSync } from 'child_process'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const ROOT = resolve(__dirname, ".."); +const ROOT = resolve(__dirname, '..'); // Color codes const COLORS = { - reset: "\x1b[0m", - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", + reset: '\x1b[0m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', }; function log(color, message) { @@ -30,34 +30,34 @@ function log(color, message) { function exec(command, options = {}) { return execSync(command, { cwd: ROOT, - stdio: options.silent ? "pipe" : "inherit", - encoding: "utf-8", + stdio: options.silent ? 'pipe' : 'inherit', + encoding: 'utf-8', ...options, }); } async function fetchLatestNightly() { - log(COLORS.yellow, "Fetching latest nightly from npm..."); + log(COLORS.yellow, 'Fetching latest nightly from npm...'); try { - const output = exec("npm view @aztec/aztec.js versions --json", { silent: true }); + const output = exec('npm view @aztec/aztec.js versions --json', { silent: true }); const versions = JSON.parse(output); - const nightlies = versions.filter((v) => v.match(/^4\.0\.0-nightly\.\d+$/)); + const nightlies = versions.filter(v => v.match(/^4\.\d+\.\d+-nightly\.\d+$/)); const latest = nightlies[nightlies.length - 1]; if (!latest) { - throw new Error("No nightly versions found"); + throw new Error('No nightly versions found'); } return latest; } catch (error) { - log(COLORS.red, "Failed to fetch latest nightly version from npm"); - log(COLORS.red, "Please specify a version with --version"); + log(COLORS.red, 'Failed to fetch latest nightly version from npm'); + log(COLORS.red, 'Please specify a version with --version'); process.exit(1); } } function updatePackageJson(version) { - log(COLORS.yellow, "[1/7] Updating package.json..."); - const path = resolve(ROOT, "package.json"); - let content = readFileSync(path, "utf-8"); + log(COLORS.yellow, '[1/7] Updating package.json...'); + const path = resolve(ROOT, 'package.json'); + let content = readFileSync(path, 'utf-8'); // Update @aztec/* dependency versions (any version tag) content = content.replace(/"(@aztec\/[^"]+)": "v[^"]+"/g, `"$1": "v${version}"`); @@ -65,41 +65,60 @@ function updatePackageJson(version) { // Update version path segments in scripts (e.g. copy:dependencies nargo paths) content = content.replace(/\/aztec-packages\/v[^/]+\//g, `/aztec-packages/v${version}/`); - writeFileSync(path, content, "utf-8"); - log(COLORS.green, "✓ package.json updated\n"); + writeFileSync(path, content, 'utf-8'); + log(COLORS.green, '✓ package.json updated\n'); } function updateNargoToml(version) { - log(COLORS.yellow, "[2/7] Updating Nargo.toml files..."); - const path = resolve(ROOT, "contracts/proof_of_password/Nargo.toml"); - let content = readFileSync(path, "utf-8"); - - content = content.replace(/(git = "https:\/\/github\.com\/AztecProtocol\/aztec-packages[^"]*",\s*tag = ")v[^"]+"/g, `$1v${version}"`); + log(COLORS.yellow, '[2/7] Updating Nargo.toml files...'); + const path = resolve(ROOT, 'contracts'); + const contracts = ['proof_of_password', 'amm']; + for (const contract of contracts) { + const nargoPath = resolve(path, contract, 'Nargo.toml'); + let content = readFileSync(nargoPath, 'utf-8'); + + content = content.replace( + /(git = "https:\/\/github\.com\/AztecProtocol\/aztec-packages[^"]*",\s*tag = ")v[^"]+"/g, + `$1v${version}"`, + ); + + writeFileSync(nargoPath, content, 'utf-8'); + } - writeFileSync(path, content, "utf-8"); - log(COLORS.green, "✓ Nargo.toml files updated\n"); + log(COLORS.green, '✓ Nargo.toml files updated\n'); } function updateReadme(version) { - log(COLORS.yellow, "[3/7] Updating README.md..."); - const path = resolve(ROOT, "README.md"); - let content = readFileSync(path, "utf-8"); + log(COLORS.yellow, '[3/7] Updating README.md...'); + const path = resolve(ROOT, 'README.md'); + let content = readFileSync(path, 'utf-8'); content = content.replace(/v4\.0\.0-nightly\.\d+/g, `v${version}`); - writeFileSync(path, content, "utf-8"); - log(COLORS.green, "✓ README.md updated\n"); + writeFileSync(path, content, 'utf-8'); + log(COLORS.green, '✓ README.md updated\n'); } function installDependencies() { - log(COLORS.yellow, "[4/7] Running yarn install..."); - exec("yarn install"); - log(COLORS.green, "✓ Dependencies installed\n"); + log(COLORS.yellow, '[4/7] Running yarn install...'); + exec('yarn install'); + log(COLORS.green, '✓ Dependencies installed\n'); } function installAztecCLI(version) { log(COLORS.yellow, `[5/7] Installing Aztec CLI version ${version}...`); + // Check if already at the right version + try { + const current = exec('aztec --version', { silent: true }).trim(); + if (current === version) { + log(COLORS.green, `✓ Aztec CLI already at v${version}, skipping\n`); + return; + } + } catch { + // aztec not installed, proceed with installation + } + const isCI = !!process.env.CI; if (isCI) { @@ -111,32 +130,35 @@ function installAztecCLI(version) { // Update PATH for current session process.env.PATH = `${process.env.HOME}/.aztec/versions/${version}/bin:${process.env.PATH}`; process.env.PATH = `${process.env.HOME}/.aztec/versions/${version}/node_modules/.bin:${process.env.PATH}`; - log(COLORS.green, "✓ Aztec CLI installed (CI mode)\n"); + log(COLORS.green, '✓ Aztec CLI installed (CI mode)\n'); } else { // Local environment with aztec-up try { - exec("command -v aztec-up", { silent: true }); + exec('command -v aztec-up', { silent: true }); exec(`aztec-up install ${version}`); - log(COLORS.green, "✓ Aztec CLI updated\n"); + log(COLORS.green, '✓ Aztec CLI updated\n'); } catch { - log(COLORS.red, `Warning: aztec-up not found in PATH. Please install manually with: aztec-up install ${version}\n`); + log( + COLORS.red, + `Warning: aztec-up not found in PATH. Please install manually with: aztec-up install ${version}\n`, + ); } } } function compileContracts() { - log(COLORS.yellow, "[6/7] Compiling contracts..."); - exec("yarn compile:contracts"); - log(COLORS.green, "✓ Contracts compiled\n"); + log(COLORS.yellow, '[6/7] Compiling contracts...'); + exec('yarn compile:contracts'); + log(COLORS.green, '✓ Contracts compiled\n'); } -const VALID_NETWORKS = ["local", "devnet", "nextnet"]; +const VALID_NETWORKS = ['local', 'devnet', 'nextnet']; function deploy(network) { log(COLORS.yellow, `[7/7] Deploying to ${network}...`); if (!process.env.PASSWORD) { - log(COLORS.red, "ERROR: PASSWORD environment variable not set"); - log(COLORS.red, "Please set PASSWORD before running with --deploy flag"); + log(COLORS.red, 'ERROR: PASSWORD environment variable not set'); + log(COLORS.red, 'Please set PASSWORD before running with --deploy flag'); process.exit(1); } exec(`yarn deploy:${network}`); @@ -144,7 +166,7 @@ function deploy(network) { } async function main() { - log(COLORS.green, "=== Gregoswap Nightly Update Script ===\n"); + log(COLORS.green, '=== Gregoswap Nightly Update Script ===\n'); // Parse arguments const args = process.argv.slice(2); @@ -153,30 +175,30 @@ async function main() { let skipAztecUp = false; for (let i = 0; i < args.length; i++) { - if (args[i] === "--version" && args[i + 1]) { - version = args[i + 1].replace(/^v/, ""); + if (args[i] === '--version' && args[i + 1]) { + version = args[i + 1].replace(/^v/, ''); i++; - } else if (args[i] === "--deploy") { + } else if (args[i] === '--deploy') { const next = args[i + 1]; - if (next && !next.startsWith("--")) { + if (next && !next.startsWith('--')) { if (!VALID_NETWORKS.includes(next)) { - log(COLORS.red, `ERROR: Unknown network "${next}". Valid networks: ${VALID_NETWORKS.join(", ")}`); + log(COLORS.red, `ERROR: Unknown network "${next}". Valid networks: ${VALID_NETWORKS.join(', ')}`); process.exit(1); } deployNetwork = next; i++; } else { - deployNetwork = "nextnet"; + deployNetwork = 'nextnet'; } - } else if (args[i] === "--skip-aztec-up") { + } else if (args[i] === '--skip-aztec-up') { skipAztecUp = true; - } else if (args[i] === "--help") { - console.log("Usage: node scripts/update-to-nightly.js [OPTIONS]"); - console.log("\nOptions:"); - console.log(" --version VERSION Specify nightly version (e.g., 4.0.0-nightly.20260206)"); - console.log(" --deploy [NETWORK] Deploy after update (default: nextnet, options: local, devnet, nextnet)"); - console.log(" --skip-aztec-up Skip Aztec CLI installation"); - console.log(" --help Show this help message"); + } else if (args[i] === '--help') { + console.log('Usage: node scripts/update-to-nightly.js [OPTIONS]'); + console.log('\nOptions:'); + console.log(' --version VERSION Specify nightly version (e.g., 4.0.0-nightly.20260206)'); + console.log(' --deploy [NETWORK] Deploy after update (default: nextnet, options: local, devnet, nextnet)'); + console.log(' --skip-aztec-up Skip Aztec CLI installation'); + console.log(' --help Show this help message'); process.exit(0); } } @@ -198,7 +220,7 @@ async function main() { if (!skipAztecUp) { installAztecCLI(version); } else { - log(COLORS.yellow, "[5/7] Skipping Aztec CLI installation (--skip-aztec-up flag set)\n"); + log(COLORS.yellow, '[5/7] Skipping Aztec CLI installation (--skip-aztec-up flag set)\n'); } compileContracts(); @@ -206,17 +228,20 @@ async function main() { if (deployNetwork) { deploy(deployNetwork); } else { - log(COLORS.yellow, "[7/7] Skipping deployment (use --deploy [network] flag to deploy)\n"); + log(COLORS.yellow, '[7/7] Skipping deployment (use --deploy [network] flag to deploy)\n'); } - log(COLORS.green, "=== Update Complete ==="); + log(COLORS.green, '=== Update Complete ==='); log(COLORS.green, `Version: v${version}`); if (!deployNetwork) { - log(COLORS.yellow, "To deploy, run: PASSWORD= node scripts/update-to-nightly.js --deploy [local|devnet|nextnet]"); + log( + COLORS.yellow, + 'To deploy, run: PASSWORD= node scripts/update-to-nightly.js --deploy [local|devnet|nextnet]', + ); } } -main().catch((error) => { +main().catch(error => { log(COLORS.red, `Error: ${error.message}`); process.exit(1); }); diff --git a/src/App.tsx b/src/App.tsx index 9e42f1d..45d8b4e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,14 @@ import { useOnboarding } from './contexts/onboarding'; import { OnboardingModal } from './components/OnboardingModal'; import { TxNotificationCenter } from '@gregojuice/embedded-wallet/ui'; import type { AztecAddress } from '@aztec/aztec.js/addresses'; +import { lazy, Suspense } from 'react'; + +// Dev-only lazy import so ProfilePanel + its deps (canvas flame chart) are +// tree-shaken from prod builds. `import.meta.env.DEV` is replaced with the +// literal boolean at build time; in prod the `?:` picks `null`. +const ProfilePanel = import.meta.env.DEV + ? lazy(() => import('./components/ProfilePanel').then(m => ({ default: m.ProfilePanel }))) + : null; export function App() { const { disconnectWallet, setCurrentAddress, currentAddress, error: walletError, isLoading: walletLoading } = useWallet(); @@ -137,6 +145,14 @@ export function App() { {/* Transaction Progress Toasts (embedded wallet only) */} + + {/* Performance profiling panel — dev only (zone.js async-context tracking + requires transpiled async/await which we enable only in dev mode). */} + {ProfilePanel && ( + + + + )} ); } diff --git a/src/app-entry.tsx b/src/app-entry.tsx new file mode 100644 index 0000000..c8cb4b1 --- /dev/null +++ b/src/app-entry.tsx @@ -0,0 +1,24 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { App } from './App.tsx'; +import { NetworkProvider } from './contexts/network/NetworkContext'; +import { WalletProvider } from './contexts/wallet/WalletContext'; +import { ContractsProvider } from './contexts/contracts/ContractsContext'; +import { SwapProvider } from './contexts/swap/SwapContext'; +import { OnboardingProvider } from './contexts/onboarding/OnboardingContext'; + +createRoot(document.getElementById('root')!).render( + + + + + + + + + + + + + , +); diff --git a/src/components/ProfilePanel.tsx b/src/components/ProfilePanel.tsx new file mode 100644 index 0000000..4003857 --- /dev/null +++ b/src/components/ProfilePanel.tsx @@ -0,0 +1,1054 @@ +/** + * Profiling panel — canvas-based waterfall chart. + * + * Activated by the `?profile` query parameter. Shows every instrumented + * wallet / PXE / simulator / oracle / node / RPC / WASM span as a + * horizontal bar on a scrollable time axis. + * + * Nesting is derived from timing containment (parent spans fully enclose + * child spans). Click a bar to expand/collapse its subtree. + * + * Interaction: + * - Click bar or label triangle: expand / collapse subtree + * - Horizontal scroll: pan the timeline + * - +/- buttons: zoom in / out (preserves scroll center) + * - Hover: tooltip with timing details + * - Min-duration slider: hide short spans + */ + +import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; +import { createPortal } from 'react-dom'; +import { Box, Button, Chip, Paper, Slider, Tooltip, Typography } from '@mui/material'; +import { profiler, type ProfileReport, type ProfileRecord, type Category } from '../profiling'; +import { useWallet } from '../contexts/wallet'; + +// ─── Constants ─────────────────────────────────────────────────────────────── + +const ROW_H = 22; +const ROW_GAP = 1; +const ROW_STEP = ROW_H + ROW_GAP; +const LABEL_W = 220; +const INDENT_PX = 14; +const FONT = '11px monospace'; +const MIN_BAR_PX = 2; +const MAX_ZOOM = 64; + +const CATEGORY_COLORS: Record = { + wallet: '#5c7cfa', + pxe: '#ce93d8', + sim: '#ffd54f', + oracle: '#ffab40', + store: '#a5d6a7', + node: '#66bb6a', + rpc: '#4fc3f7', + wasm: '#ff7043', +}; + +/** Format milliseconds as human-readable duration. */ +const fmt = (ms: number) => + ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms.toFixed(1)}ms`; + +// ─── Tree model ────────────────────────────────────────────────────────────── + +interface TreeNode { + id: string; + record: ProfileRecord; + children: TreeNode[]; + depth: number; +} + +/** + * Build a tree from flat records using explicit parent IDs. + * + * Each record's `parentId` comes from async-context tracking (zone.js) and + * reflects the real causal chain — no timing heuristics, no peer rules, + * no category hierarchies needed. A parallel call to the same method + * produces a sibling, not a coincidentally-nested child. + * + * Records whose `parentId` isn't present in the set (e.g. the parent was + * filtered out, or the profile started mid-operation) become roots. + */ +function buildTree(records: ProfileRecord[]): TreeNode[] { + if (records.length === 0) return []; + + const byId = new Map(); + for (const r of records) { + byId.set(r.id, { id: r.id, record: r, children: [], depth: 0 }); + } + + const roots: TreeNode[] = []; + for (const node of byId.values()) { + const parent = node.record.parentId ? byId.get(node.record.parentId) : undefined; + if (parent) { + parent.children.push(node); + } else { + roots.push(node); + } + } + + // Depth-first fill in `depth` for rendering. + function assignDepth(node: TreeNode, d: number) { + node.depth = d; + for (const child of node.children) assignDepth(child, d + 1); + } + for (const root of roots) assignDepth(root, 0); + + return roots; +} + +// ─── Layout ────────────────────────────────────────────────────────────────── + +interface LayoutItem { + record: ProfileRecord; + nodeId: string; + depth: number; + row: number; + hasChildren: boolean; +} + +/** + * Flatten the tree into row assignments, respecting the expanded set. + * + * Children are grouped into temporal clusters (overlapping children form a + * group). Each group is rendered in chronological order: + * - All-leaf groups are lane-packed for compactness. + * - Mixed / block groups render each child in time order. + */ +function layoutTree( + roots: TreeNode[], + expanded: Set, +): { items: LayoutItem[]; expandableIds: Set } { + const items: LayoutItem[] = []; + const expandableIds = new Set(); + let nextRow = 0; + + function isLeafLike(node: TreeNode): boolean { + return node.children.length === 0; + } + + function visitNode(node: TreeNode) { + if (node.children.length > 0) expandableIds.add(node.id); + + items.push({ + record: node.record, + nodeId: node.id, + depth: node.depth, + row: nextRow, + hasChildren: node.children.length > 0, + }); + nextRow++; + + if (!expanded.has(node.id) || node.children.length === 0) return; + + const children = [...node.children].sort( + (a, b) => a.record.start - b.record.start, + ); + + // Group children into temporal clusters so parallel calls stay adjacent. + type Group = { nodes: TreeNode[]; end: number }; + const groups: Group[] = []; + for (const child of children) { + const childEnd = child.record.start + child.record.duration; + const last = groups[groups.length - 1]; + if (last && child.record.start < last.end) { + last.nodes.push(child); + last.end = Math.max(last.end, childEnd); + } else { + groups.push({ nodes: [child], end: childEnd }); + } + } + + for (const group of groups) { + const allLeafLike = group.nodes.every(c => isLeafLike(c)); + + if (allLeafLike) { + // Lane-pack leaves for compactness. + const laneEnds: number[] = []; + const baseRow = nextRow; + for (const child of group.nodes) { + const end = child.record.start + child.record.duration; + let lane = laneEnds.findIndex(e => child.record.start >= e); + if (lane === -1) { lane = laneEnds.length; laneEnds.push(0); } + laneEnds[lane] = end; + items.push({ + record: child.record, nodeId: child.id, + depth: child.depth, row: baseRow + lane, hasChildren: false, + }); + } + nextRow += Math.max(1, laneEnds.length); + } else { + // Mixed group — each child in time order. + for (const child of group.nodes) { + if (isLeafLike(child)) { + items.push({ + record: child.record, nodeId: child.id, + depth: child.depth, row: nextRow, hasChildren: false, + }); + nextRow++; + } else { + visitNode(child); + } + } + } + } + } + + const sorted = [...roots].sort((a, b) => a.record.start - b.record.start); + for (const root of sorted) visitNode(root); + return { items, expandableIds }; +} + +// ─── Waterfall chart ───────────────────────────────────────────────────────── + +interface HoverInfo { + item: LayoutItem; + x: number; + y: number; +} + +/** + * Waterfall chart with virtual rendering. + * + * The chart canvases are always viewport-sized — never exceeding browser + * canvas limits (~16k px per side). Panning and zooming translate the + * drawing offsets rather than resizing the canvas. This means there is + * effectively no limit on zoom level or number of visible rows. + */ +const CHART_HEIGHT = 600; // fixed chart viewport height + +function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDuration: number }) { + const labelCanvasRef = useRef(null); + const chartCanvasRef = useRef(null); + const outerRef = useRef(null); + const [hover, setHover] = useState(null); + const [viewportW, setViewportW] = useState(800); + const [zoom, setZoom] = useState(1); + const [expanded, setExpanded] = useState>(new Set()); + const [scrollLeft, setScrollLeft] = useState(0); + const [scrollTop, setScrollTop] = useState(0); + const dragRef = useRef<{ + clientX0: number; clientY0: number; + scrollLeft0: number; scrollTop0: number; + dragStartX: number; // canvas-space x where drag started + shiftKey: boolean; moved: boolean; + } | null>(null); + const [dragX, setDragX] = useState<{ x0: number; x1: number } | null>(null); + + // ── Data pipeline ────────────────────────────────────────────────────────── + + const filtered = useMemo( + () => report.records.filter(r => r.duration >= minDuration), + [report, minDuration], + ); + + const tree = useMemo(() => buildTree(filtered), [filtered]); + + const childrenMap = useMemo(() => { + const cMap = new Map(); + function walk(node: TreeNode) { + cMap.set(node.id, node.children.map(c => c.id)); + for (const child of node.children) walk(child); + } + for (const root of tree) walk(root); + return cMap; + }, [tree]); + + const toggleExpand = useCallback((nodeId: string) => { + setExpanded(prev => { + const next = new Set(prev); + if (next.has(nodeId)) { + // Collapse this node and all its descendants. + next.delete(nodeId); + const removeDesc = (id: string) => { + for (const child of childrenMap.get(id) ?? []) { + next.delete(child); + removeDesc(child); + } + }; + removeDesc(nodeId); + } else { + next.add(nodeId); + } + return next; + }); + }, [childrenMap]); + + const { items: layout, expandableIds } = useMemo( + () => layoutTree(tree, expanded), + [tree, expanded], + ); + const layoutRef = useRef(layout); + layoutRef.current = layout; + + // ── Dimensions ───────────────────────────────────────────────────────────── + // + // virtualContentH: total logical height of all rows (not the canvas height) + // virtualContentW: total logical width at current zoom (not the canvas width) + // chartViewportW: visible chart area width (canvas width) + // CHART_HEIGHT: visible chart area height (canvas height) + + const numRows = layout.length > 0 ? Math.max(...layout.map(l => l.row)) + 1 : 1; + const virtualContentH = numRows * ROW_STEP + 4; + const totalMs = report.durationMs; + const chartViewportW = Math.max(1, viewportW - LABEL_W); + const virtualContentW = chartViewportW * zoom; + const maxScrollLeft = Math.max(0, virtualContentW - chartViewportW); + const maxScrollTop = Math.max(0, virtualContentH - CHART_HEIGHT); + + // ── Resize observer ──────────────────────────────────────────────────────── + + useEffect(() => { + const el = outerRef.current; + if (!el) return; + const ro = new ResizeObserver(entries => setViewportW(entries[0].contentRect.width)); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + // Clamp scroll offsets when content/viewport changes. + useEffect(() => { + setScrollLeft(s => Math.min(s, maxScrollLeft)); + setScrollTop(s => Math.min(s, maxScrollTop)); + }, [maxScrollLeft, maxScrollTop]); + + // ── Zoom with scroll-center preservation ─────────────────────────────────── + + const doZoom = useCallback((nextZoom: number) => { + // Preserve the center of the current viewport. + const centerRatio = virtualContentW > 0 + ? (scrollLeft + chartViewportW / 2) / virtualContentW + : 0.5; + const newVirtualW = chartViewportW * nextZoom; + const newScrollLeft = centerRatio * newVirtualW - chartViewportW / 2; + const clamped = Math.max(0, Math.min(newVirtualW - chartViewportW, newScrollLeft)); + setZoom(nextZoom); + setScrollLeft(clamped); + }, [scrollLeft, virtualContentW, chartViewportW]); + + // ── Draw label canvas (virtualized: vertical scroll offset applied) ─────── + + useEffect(() => { + const canvas = labelCanvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const dpr = window.devicePixelRatio || 1; + canvas.width = LABEL_W * dpr; + canvas.height = CHART_HEIGHT * dpr; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + ctx.clearRect(0, 0, LABEL_W, CHART_HEIGHT); + ctx.font = FONT; + + // Only draw rows visible in the current viewport. + const firstRow = Math.floor(scrollTop / ROW_STEP); + const lastRow = Math.min(numRows, Math.ceil((scrollTop + CHART_HEIGHT) / ROW_STEP)); + + // Row backgrounds + for (let r = firstRow; r < lastRow; r++) { + const y = r * ROW_STEP - scrollTop; + ctx.fillStyle = r % 2 === 0 ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0)'; + ctx.fillRect(0, y, LABEL_W, ROW_STEP); + } + + // Separator + ctx.strokeStyle = 'rgba(255,255,255,0.08)'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(LABEL_W - 0.5, 0); + ctx.lineTo(LABEL_W - 0.5, CHART_HEIGHT); + ctx.stroke(); + + // Labels (only visible rows) + const drawnRows = new Set(); + for (const item of layout) { + if (item.row < firstRow || item.row >= lastRow) continue; + if (drawnRows.has(item.row)) continue; + drawnRows.add(item.row); + + const y = item.row * ROW_STEP + 1 - scrollTop; + const indent = item.depth * INDENT_PX; + const labelX = 4 + indent; + const cy = y + ROW_H / 2; + + // Expand/collapse triangle — bright so it's clearly clickable. + if (item.hasChildren) { + const isExp = expanded.has(item.nodeId); + ctx.fillStyle = isExp ? 'rgba(212,255,40,0.85)' : 'rgba(255,255,255,0.75)'; + ctx.beginPath(); + if (isExp) { + // ▼ expanded + ctx.moveTo(labelX, cy - 3); + ctx.lineTo(labelX + 8, cy - 3); + ctx.lineTo(labelX + 4, cy + 4); + } else { + // ▶ collapsed + ctx.moveTo(labelX, cy - 4); + ctx.lineTo(labelX, cy + 4); + ctx.lineTo(labelX + 7, cy); + } + ctx.closePath(); + ctx.fill(); + } + + // Category-colored swatch before the label name. + const swatchX = labelX + (item.hasChildren ? 11 : 0); + const swatchColor = item.record.error ? '#e53935' : CATEGORY_COLORS[item.record.category]; + ctx.fillStyle = swatchColor; + ctx.fillRect(swatchX, y + ROW_H / 2 - 3, 6, 6); + + const textX = swatchX + 10; + ctx.fillStyle = 'rgba(255,255,255,0.65)'; + ctx.save(); + ctx.beginPath(); + ctx.rect(textX, y + 2, LABEL_W - textX - 6, ROW_H - 2); + ctx.clip(); + ctx.fillText(item.record.name, textX, y + ROW_H - 6); + ctx.restore(); + } + }, [layout, numRows, expanded, scrollTop]); + + // ── Draw chart canvas (virtualized: viewport-sized, drawing translated) ── + + useEffect(() => { + const canvas = chartCanvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const dpr = window.devicePixelRatio || 1; + canvas.width = chartViewportW * dpr; + canvas.height = CHART_HEIGHT * dpr; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + ctx.clearRect(0, 0, chartViewportW, CHART_HEIGHT); + ctx.font = FONT; + + // Map ms to virtual x, then subtract scrollLeft to get canvas x. + const msToX = (ms: number) => (ms / totalMs) * virtualContentW - scrollLeft; + + // Only draw rows visible in the current vertical viewport. + const firstRow = Math.floor(scrollTop / ROW_STEP); + const lastRow = Math.min(numRows, Math.ceil((scrollTop + CHART_HEIGHT) / ROW_STEP)); + + // Row backgrounds + for (let r = firstRow; r < lastRow; r++) { + const y = r * ROW_STEP - scrollTop; + ctx.fillStyle = r % 2 === 0 ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0)'; + ctx.fillRect(0, y, chartViewportW, ROW_STEP); + } + + // Grid lines + time labels. + // Only draw grid within the visible time window. + const visibleMsStart = (scrollLeft / virtualContentW) * totalMs; + const visibleMsEnd = ((scrollLeft + chartViewportW) / virtualContentW) * totalMs; + const visibleMs = visibleMsEnd - visibleMsStart; + const rawStep = visibleMs / 6; + const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); + const step = [1, 2, 5, 10].map(n => n * mag).find(s => visibleMs / s <= 8) ?? mag; + const firstTick = Math.ceil(visibleMsStart / step) * step; + ctx.strokeStyle = 'rgba(255,255,255,0.05)'; + ctx.lineWidth = 1; + for (let t = firstTick; t <= visibleMsEnd; t += step) { + const x = Math.round(msToX(t)) + 0.5; + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, CHART_HEIGHT); + ctx.stroke(); + ctx.fillStyle = 'rgba(255,255,255,0.25)'; + ctx.fillText(fmt(t), x + 3, 10); + } + + // Bars — skip those outside the visible time and row ranges. + for (const item of layout) { + if (item.row < firstRow || item.row >= lastRow) continue; + const r = item.record; + const x = msToX(r.start); + const w = Math.max(MIN_BAR_PX, msToX(r.start + r.duration) - x); + if (x + w < 0 || x > chartViewportW) continue; + const y = item.row * ROW_STEP + 1 - scrollTop; + const color = CATEGORY_COLORS[r.category]; + + ctx.fillStyle = r.error ? '#e53935' : color; + ctx.globalAlpha = r.category === 'wasm' ? 0.7 : 0.85; + ctx.fillRect(x, y, w, ROW_H - 1); + ctx.globalAlpha = 1; + + // Expand indicator on the bar: small ▶/▼ at the left edge for expandable spans. + if (item.hasChildren && w > 12) { + const isExp = expanded.has(item.nodeId); + const ix = Math.max(0, x) + 3; + const iy = y + ROW_H / 2; + ctx.fillStyle = 'rgba(0,0,0,0.6)'; + ctx.beginPath(); + if (isExp) { + ctx.moveTo(ix, iy - 2); ctx.lineTo(ix + 5, iy - 2); ctx.lineTo(ix + 2.5, iy + 3); + } else { + ctx.moveTo(ix, iy - 3); ctx.lineTo(ix, iy + 3); ctx.lineTo(ix + 4, iy); + } + ctx.closePath(); + ctx.fill(); + } + + if (w > 50) { + const textOffset = item.hasChildren ? 10 : 3; + ctx.fillStyle = 'rgba(0,0,0,0.85)'; + ctx.save(); + ctx.beginPath(); + ctx.rect(Math.max(0, x) + textOffset, y, Math.min(chartViewportW, x + w) - Math.max(0, x) - textOffset - 2, ROW_H); + ctx.clip(); + ctx.fillText(`${r.name} ${fmt(r.duration)}`, Math.max(0, x) + textOffset, y + ROW_H - 6); + ctx.restore(); + } + } + }, [layout, numRows, chartViewportW, virtualContentW, totalMs, zoom, scrollLeft, scrollTop]); + + // ── Hit test helpers ─────────────────────────────────────────────────────── + // + // Coordinate systems: + // client: browser viewport coordinates (from mouse events) + // canvas: chart canvas coordinates (0 to chartViewportW × CHART_HEIGHT) + // virtual: full logical chart coordinates (0 to virtualContentW × virtualContentH) + // + // canvas_x = virtual_x - scrollLeft + // canvas_y = virtual_y - scrollTop + + /** Convert a mouse event to a virtual (logical) x coordinate in the chart. */ + const toVirtualX = useCallback((e: React.MouseEvent): number => { + const canvas = chartCanvasRef.current; + if (!canvas) return 0; + const rect = canvas.getBoundingClientRect(); + const canvasX = (e.clientX - rect.left) * (chartViewportW / rect.width); + return canvasX + scrollLeft; + }, [chartViewportW, scrollLeft]); + + const chartHitTest = useCallback((e: React.MouseEvent): LayoutItem | null => { + const canvas = chartCanvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + const canvasX = (e.clientX - rect.left) * (chartViewportW / rect.width); + const canvasY = e.clientY - rect.top; + const virtualX = canvasX + scrollLeft; + const virtualY = canvasY + scrollTop; + const ms = (virtualX / virtualContentW) * totalMs; + const row = Math.floor(virtualY / ROW_STEP); + let best: LayoutItem | null = null; + for (const item of layoutRef.current) { + if (item.row !== row) continue; + const r = item.record; + if (ms >= r.start && ms <= r.start + r.duration) { + if (!best || r.duration < best.record.duration) best = item; + } + } + return best; + }, [chartViewportW, virtualContentW, totalMs, scrollLeft, scrollTop]); + + const labelHitTest = useCallback((e: React.MouseEvent): LayoutItem | null => { + const canvas = labelCanvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + const canvasY = e.clientY - rect.top; + const virtualY = canvasY + scrollTop; + const row = Math.floor(virtualY / ROW_STEP); + for (const item of layoutRef.current) { + if (item.row === row && item.hasChildren) return item; + } + return null; + }, [scrollTop]); + + // ── Event handlers ───────────────────────────────────────────────────────── + + const onLabelClick = useCallback((e: React.MouseEvent) => { + const item = labelHitTest(e); + if (item) toggleExpand(item.nodeId); + }, [labelHitTest, toggleExpand]); + + const onLabelMouseMove = useCallback((e: React.MouseEvent) => { + const canvas = labelCanvasRef.current; + if (!canvas) return; + const item = labelHitTest(e); + canvas.style.cursor = item ? 'pointer' : 'default'; + }, [labelHitTest]); + + const onChartMouseDown = useCallback((e: React.MouseEvent) => { + if (e.button !== 0) return; + dragRef.current = { + clientX0: e.clientX, + clientY0: e.clientY, + scrollLeft0: scrollLeft, + scrollTop0: scrollTop, + dragStartX: toVirtualX(e), + shiftKey: e.shiftKey, + moved: false, + }; + setDragX(null); + setHover(null); + }, [scrollLeft, scrollTop, toVirtualX]); + + const onChartMouseMove = useCallback((e: React.MouseEvent) => { + const canvas = chartCanvasRef.current; + if (!canvas) return; + + if (dragRef.current) { + const dx = e.clientX - dragRef.current.clientX0; + const dy = e.clientY - dragRef.current.clientY0; + if (Math.abs(dx) > 3 || Math.abs(dy) > 3) dragRef.current.moved = true; + + if (dragRef.current.shiftKey) { + // Shift+drag: zoom selection. Paint overlay in canvas coords. + if (dragRef.current.moved) { + const rect = canvas.getBoundingClientRect(); + const x0Canvas = dragRef.current.dragStartX - scrollLeft; + const x1Canvas = (e.clientX - rect.left) * (chartViewportW / rect.width); + setDragX({ x0: x0Canvas, x1: x1Canvas }); + } + canvas.style.cursor = 'col-resize'; + } else { + // Plain drag: pan both axes via virtual scroll state. + const newScrollLeft = Math.max(0, Math.min(maxScrollLeft, dragRef.current.scrollLeft0 - dx)); + const newScrollTop = Math.max(0, Math.min(maxScrollTop, dragRef.current.scrollTop0 - dy)); + setScrollLeft(newScrollLeft); + setScrollTop(newScrollTop); + canvas.style.cursor = 'grabbing'; + } + return; + } + + const item = chartHitTest(e); + canvas.style.cursor = item ? 'pointer' : 'grab'; + if (item) { + setHover({ item, x: e.clientX, y: e.clientY }); + } else { + setHover(null); + } + }, [chartHitTest, chartViewportW, scrollLeft, maxScrollLeft, maxScrollTop]); + + const onChartMouseUp = useCallback((e: React.MouseEvent) => { + if (!dragRef.current) return; + const { dragStartX, moved, shiftKey } = dragRef.current; + const endVirtualX = toVirtualX(e); + dragRef.current = null; + setDragX(null); + + if (!moved) { + // Single click → expand/collapse + const item = chartHitTest(e); + if (item?.hasChildren) toggleExpand(item.nodeId); + return; + } + + if (!shiftKey) return; // plain drag (pan) — nothing to finalize + + // Shift+drag-to-zoom: compute ms range from virtual coordinates. + const lo = Math.min(dragStartX, endVirtualX); + const hi = Math.max(dragStartX, endVirtualX); + const msStart = (lo / virtualContentW) * totalMs; + const msEnd = (hi / virtualContentW) * totalMs; + const msRange = msEnd - msStart; + if (msRange <= 0) return; + + const newZoom = Math.max(1, totalMs / msRange); + const newVirtualW = chartViewportW * newZoom; + const newScrollLeft = Math.max(0, (msStart / totalMs) * newVirtualW); + setZoom(newZoom); + setScrollLeft(newScrollLeft); + }, [toVirtualX, chartHitTest, toggleExpand, chartViewportW, virtualContentW, totalMs]); + + const onChartMouseLeave = useCallback(() => { + if (dragRef.current) { + dragRef.current = null; + setDragX(null); + } + setHover(null); + }, []); + + // Wheel: horizontal → scroll horizontal; vertical → scroll vertical. + // Ctrl/Cmd+wheel → zoom in/out at cursor position. + const onChartWheel = useCallback((e: React.WheelEvent) => { + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + const factor = e.deltaY < 0 ? 1.25 : 1 / 1.25; + const rect = chartCanvasRef.current!.getBoundingClientRect(); + const cursorCanvasX = (e.clientX - rect.left) * (chartViewportW / rect.width); + const cursorVirtualX = cursorCanvasX + scrollLeft; + const cursorRatio = virtualContentW > 0 ? cursorVirtualX / virtualContentW : 0; + + const newZoom = Math.max(1, zoom * factor); + const newVirtualW = chartViewportW * newZoom; + // Keep the point under the cursor stationary. + const newScrollLeft = cursorRatio * newVirtualW - cursorCanvasX; + setZoom(newZoom); + setScrollLeft(Math.max(0, Math.min(newVirtualW - chartViewportW, newScrollLeft))); + return; + } + // Pan + setScrollLeft(s => Math.max(0, Math.min(maxScrollLeft, s + e.deltaX))); + setScrollTop(s => Math.max(0, Math.min(maxScrollTop, s + e.deltaY))); + }, [zoom, chartViewportW, virtualContentW, scrollLeft, maxScrollLeft, maxScrollTop]); + + // ── Render ───────────────────────────────────────────────────────────────── + + const btnSx = { fontSize: 9, py: 0, px: 0.5, minWidth: 0, color: 'rgba(255,255,255,0.5)' }; + + return ( + + {/* Toolbar */} + + + {fmt(totalMs)} total · {filtered.length} spans + {zoom > 1 ? ` \u00b7 ${zoom}x zoom` : ''} +  · drag to pan · wheel to scroll · ctrl+wheel or shift+drag to zoom + + + + {zoom > 1 && ( + + )} + {expanded.size > 0 && ( + + )} + {expanded.size < expandableIds.size && expandableIds.size > 0 && ( + + )} + + + {/* Chart: fixed viewport with virtual rendering — no native scrollbars. */} + + {/* Label column */} + + + + {/* Chart viewport */} + + + {dragX && (() => { + const left = Math.min(dragX.x0, dragX.x1); + const width = Math.abs(dragX.x1 - dragX.x0); + return ( + + ); + })()} + + + + {/* Legend */} + + {(Object.entries(CATEGORY_COLORS) as [Category, string][]).map(([cat, color]) => ( + + + {cat} + + ))} + + + {/* Tooltip */} + {hover && } + + ); +} + +// ─── Tooltip ───────────────────────────────────────────────────────────────── + +function SpanTooltip({ info, totalMs }: { info: HoverInfo; totalMs: number }) { + const { record } = info.item; + const color = record.error ? '#e53935' : CATEGORY_COLORS[record.category]; + return ( + + + {record.name} + + + {record.category} · {fmt(record.start)} → {fmt(record.start + record.duration)} · {fmt(record.duration)} · {((record.duration / totalMs) * 100).toFixed(1)}% + + {record.detail && ( + + {record.detail} + + )} + + ); +} + +// ─── Summary table ─────────────────────────────────────────────────────────── + +/** Aggregate records by name within a category. */ +function aggregateByName(records: ProfileRecord[], category: Category) { + const agg = new Map(); + for (const r of records) { + if (r.category !== category) continue; + const entry = agg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; + entry.count++; + entry.totalMs += r.duration; + if (r.duration > entry.maxMs) entry.maxMs = r.duration; + agg.set(r.name, entry); + } + return [...agg.entries()].sort((a, b) => b[1].totalMs - a[1].totalMs); +} + +function SummaryTable({ report, minDuration }: { report: ProfileReport; minDuration: number }) { + const filtered = report.records.filter(r => r.duration >= minDuration); + + const byCat = new Map(); + for (const r of filtered) { + const entry = byCat.get(r.category) ?? { count: 0, totalMs: 0 }; + entry.count++; + entry.totalMs += r.duration; + byCat.set(r.category, entry); + } + + const allSim = aggregateByName(report.records, 'sim'); + const topWasm = aggregateByName(report.records, 'wasm').slice(0, 15); + const topRpc = aggregateByName(report.records, 'rpc').slice(0, 15); + + const td = { fontSize: 10, color: 'rgba(255,255,255,0.6)', fontFamily: 'monospace', py: 0.25, px: 1 }; + const tdR = { ...td, textAlign: 'right' as const }; + const tdDim = { ...tdR, color: 'rgba(255,255,255,0.35)' }; + + const AggTable = ({ rows, color, max = 15 }: { + rows: [string, { count: number; totalMs: number; maxMs: number }][]; + color?: string; + max?: number; + }) => ( + + + {rows.slice(0, max).map(([name, { count, totalMs, maxMs }]) => ( + + {name} + ×{count} + {fmt(totalMs)} + max {fmt(maxMs)} + + ))} + + + ); + + const SectionTitle = ({ children }: { children: string }) => ( + + {children} + + ); + + return ( + + + BY CATEGORY + + + {([...byCat.entries()] as [Category, { count: number; totalMs: number }][]) + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .map(([cat, { count, totalMs }]) => ( + + {cat} + {count} + {fmt(totalMs)} + + ))} + + + + + {allSim.length > 0 && ( + + CIRCUIT EXECUTIONS + + + )} + + {topWasm.length > 0 && ( + + TOP WASM + + + )} + + {topRpc.length > 0 && ( + + TOP RPC + + + )} + + ); +} + +// ─── Full-screen profile page ──────────────────────────────────────────────── + +function ProfilePage({ report, onClose }: { report: ProfileReport; onClose: () => void }) { + const [minDuration, setMinDuration] = useState(0.5); + + useEffect(() => { + const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, [onClose]); + + return createPortal( + + {/* Header */} + + + PROFILE — {report.name} — {fmt(report.durationMs)} + + + + + + min {fmt(minDuration)} + + setMinDuration(v as number)} + sx={{ width: 100, color: 'rgba(212,255,40,0.5)', '& .MuiSlider-thumb': { width: 10, height: 10 } }} + /> + + + + + + {/* Body */} + + + + + , + document.body, + ); +} + +// ─── Main panel — compact pill ────────────────────────────────────────────── + +export function ProfilePanel() { + const [recording, setRecording] = useState(false); + const [report, setReport] = useState(null); + const [fullscreen, setFullscreen] = useState(false); + const instrumentedRef = useRef(false); + const { wallet } = useWallet(); + + useEffect(() => { + profiler.install(); + }, []); + + useEffect(() => { + if (!wallet || instrumentedRef.current) return; + profiler.instrumentWallet(wallet); + instrumentedRef.current = true; + console.info('[profiler] Wallet instrumented'); + }, [wallet]); + + const handleToggle = useCallback(() => { + if (recording) { + const r = profiler.stop(); + setReport(r); + setRecording(false); + setFullscreen(true); + } else { + profiler.start('profile'); + setRecording(true); + setReport(null); + } + }, [recording]); + + return ( + <> + + + + PROF + + + {report && !recording && ( + <> + setFullscreen(true)} + sx={{ fontSize: 9, height: 18, cursor: 'pointer', backgroundColor: 'rgba(212,255,40,0.15)', color: 'rgba(212,255,40,0.9)', '&:hover': { backgroundColor: 'rgba(212,255,40,0.25)' } }} + /> + + + + + + + + )} + + + {fullscreen && report && setFullscreen(false)} />} + + ); +} diff --git a/src/components/swap/SwapContainer.tsx b/src/components/swap/SwapContainer.tsx index 8d55341..096aab3 100644 --- a/src/components/swap/SwapContainer.tsx +++ b/src/components/swap/SwapContainer.tsx @@ -51,7 +51,7 @@ export function SwapContainer() { dismissError: dismissSwapError, } = useSwap(); - const subscriptionStatus = useSubscriptionStatus(isSwapping, swapPhase, dripPhase); + const subscriptionStatus = useSubscriptionStatus(swapPhase, dripPhase); const isBlocked = subscriptionStatus.kind === 'full' || subscriptionStatus.kind === 'depleted'; // Drip success banner @@ -108,7 +108,8 @@ export function SwapContainer() { } }, [onboardingStatus, refetchBalances]); - // Refetch balances when swap succeeds + // Batched refresh after swap or drip success — single wallet roundtrip for + // exchange rate + balances + subscription status useEffect(() => { if (swapPhase === 'success') { refetchBalances(); diff --git a/src/config/networks/testnet.json b/src/config/networks/testnet.json index c083900..eeb7394 100644 --- a/src/config/networks/testnet.json +++ b/src/config/networks/testnet.json @@ -4,26 +4,26 @@ "chainId": "11155111", "rollupVersion": "4127419662", "contracts": { - "gregoCoin": "0x0b3afb5199e3da613b24e15a4428b49ce02b1ca3d6825d4b5b2ac96fb588cd17", - "gregoCoinPremium": "0x266186b7ba722955f9372f22238b6d3c2c801519bf826dc431f19d37778a5fb8", - "amm": "0x18dbac86293e725cc506fce4385eafb0d3ab2d5b8c46b92bfb333bb13599aa56", - "liquidityToken": "0x0b98a82ed867003db0695e0c17623280eea48da4681372511c34811a2195da4d", - "pop": "0x10c70f9633d9f97d1e25686f90082e54b5bc22cee82679e3ebd3ad92bbe83c56", - "sponsoredFPC": "0x1de1871fbfc12836e9bfe432ea3fc8a8f8d24a4feb711842b77e1a348698f037", - "salt": "0x275976fe41f7e74ccec34a4c1fceeeb912fee5ee444055610c2e43eec296c005" + "gregoCoin": "0x2a6921ec2b9d6c463c427e207f78b2bbf0b36d5878923273ed6a9adc26d9bc2b", + "gregoCoinPremium": "0x2c39517fa2194b5cc0fc82737745bffd500210b3d429cdaf3330fd6b7ca11373", + "amm": "0x181b971d54d29179cf25b3b69f79b028ea991fbf97cc8d4065d64bc53018932a", + "liquidityToken": "0x2d49c7bcbb1b42cebfbb99dbb47ff3468fc3256141d286b685121971ac6e14df", + "pop": "0x0cc4ceb7cf7cc514ea8fbc5dc7866bedfca6d42bbe4c20a4ad16f4ecb21d4c84", + "sponsoredFPC": "0x254082b62f9108d044b8998f212bb145619d91bfcd049461d74babb840181257", + "salt": "0x21ccf858cdb7ba2e24cb084cd2853658f39e30951f0ed58a6ca57e848427804c" }, "deployer": { - "address": "0x11a368aad6034a488691260cc3d03f5c1abcd49b121638e3d633a4c83c00e914" + "address": "0x12952c3d8a4116782ee85b93c634afbd4cdf2beabedd8258e89737616c269c33" }, - "deployedAt": "2026-04-10T08:58:24.559Z", + "deployedAt": "2026-04-16T07:16:45.008Z", "subscriptionFPC": { - "address": "0x2615b16dd7a23615145f889975cecc4a70beff5e799eef2b70bab329b4aa6fbc", - "secretKey": "0x074cd289d45081a02dbc0e7339705e0e6faa4a34bbc7e9d0b90363e434c2f044", + "address": "0x23fae3f4cdad2f6b36838eaf5020dd6817ff736ebcc2c7f7fa5a9e8f09b57f2f", + "secretKey": "0x061f4db024da8aa7a3eea724beee9dd6c78e4b51d2bca3bf95f8a5d1da94e594", "functions": { - "0x10c70f9633d9f97d1e25686f90082e54b5bc22cee82679e3ebd3ad92bbe83c56": { + "0x0cc4ceb7cf7cc514ea8fbc5dc7866bedfca6d42bbe4c20a4ad16f4ecb21d4c84": { "0xa539bd29": 0 }, - "0x18dbac86293e725cc506fce4385eafb0d3ab2d5b8c46b92bfb333bb13599aa56": { + "0x181b971d54d29179cf25b3b69f79b028ea991fbf97cc8d4065d64bc53018932a": { "0xfd228669": 0 } } diff --git a/src/contexts/contracts/ContractsContext.tsx b/src/contexts/contracts/ContractsContext.tsx index d7ae7a6..2eaf7dc 100644 --- a/src/contexts/contracts/ContractsContext.tsx +++ b/src/contexts/contracts/ContractsContext.tsx @@ -7,6 +7,7 @@ import { createContext, useContext, useEffect, type ReactNode, useCallback } fro import type { AztecAddress } from '@aztec/aztec.js/addresses'; import type { TxReceipt } from '@aztec/stdlib/tx'; import type { AMMContract } from '../../../contracts/target/AMM'; +import type { SubscriptionFPC } from '@gregojuice/contracts/subscription-fpc'; import { useWallet } from '../wallet'; import { useNetwork } from '../network'; import * as contractService from '../../services/contractService'; @@ -21,6 +22,7 @@ interface ContractsContextType { // Utility methods getAmm: () => AMMContract | null; + getFpc: () => SubscriptionFPC | null; getExchangeRate: () => Promise; swap: (amountOut: number, amountInMax: number) => Promise; unsponsoredSwap: (amountOut: number, amountInMax: number) => Promise; @@ -88,6 +90,11 @@ export function ContractsProvider({ children }: ContractsProviderProps) { return state.contracts.amm ?? null; }, [state.contracts.amm]); + // Get FPC wrapper instance (for hooks that need it) + const getFpc = useCallback((): SubscriptionFPC | null => { + return state.contracts.fpc ?? null; + }, [state.contracts.fpc]); + // Get exchange rate const getExchangeRate = useCallback(async (): Promise => { if ( @@ -106,6 +113,7 @@ export function ContractsProvider({ children }: ContractsProviderProps) { gregoCoin: state.contracts.gregoCoin, gregoCoinPremium: state.contracts.gregoCoinPremium, amm: state.contracts.amm, + fpc: state.contracts.fpc, }, currentAddress, ); @@ -119,17 +127,18 @@ export function ContractsProvider({ children }: ContractsProviderProps) { !currentAddress || !state.contracts.amm || !state.contracts.gregoCoin || - !state.contracts.gregoCoinPremium + !state.contracts.gregoCoinPremium || + !state.contracts.fpc ) { throw new Error('Contracts not initialized'); } return contractService.executeSponsoredSwap( - wallet, activeNetwork, state.contracts.amm, state.contracts.gregoCoin, state.contracts.gregoCoinPremium, + state.contracts.fpc, currentAddress, amountOut, amountInMax, @@ -156,6 +165,7 @@ export function ContractsProvider({ children }: ContractsProviderProps) { gregoCoin: state.contracts.gregoCoin, gregoCoinPremium: state.contracts.gregoCoinPremium, amm: state.contracts.amm, + fpc: state.contracts.fpc, }, currentAddress, amountOut, @@ -177,6 +187,7 @@ export function ContractsProvider({ children }: ContractsProviderProps) { gregoCoin: state.contracts.gregoCoin, gregoCoinPremium: state.contracts.gregoCoinPremium, amm: state.contracts.amm!, + fpc: state.contracts.fpc, }, currentAddress, ); @@ -200,6 +211,7 @@ export function ContractsProvider({ children }: ContractsProviderProps) { gregoCoin: state.contracts.gregoCoin, gregoCoinPremium: state.contracts.gregoCoinPremium, amm: state.contracts.amm, + fpc: state.contracts.fpc, }, currentAddress, ); @@ -210,13 +222,20 @@ export function ContractsProvider({ children }: ContractsProviderProps) { // Execute drip const drip = useCallback( async (password: string, recipient: AztecAddress): Promise => { - if (!wallet || !node || !state.contracts.pop) { + if (!wallet || !node || !state.contracts.pop || !state.contracts.fpc) { throw new Error('ProofOfPassword contract not initialized'); } - return contractService.executeDrip(wallet, activeNetwork, state.contracts.pop, password, recipient); + return contractService.executeDrip( + wallet, + activeNetwork, + state.contracts.pop, + state.contracts.fpc, + password, + recipient, + ); }, - [wallet, activeNetwork, state.contracts.pop], + [wallet, activeNetwork, state.contracts.pop, state.contracts.fpc], ); // Initialize contracts for embedded wallet @@ -247,6 +266,7 @@ export function ContractsProvider({ children }: ContractsProviderProps) { registerBaseContracts, registerDripContracts, getAmm, + getFpc, getExchangeRate, swap, unsponsoredSwap, diff --git a/src/contexts/contracts/reducer.ts b/src/contexts/contracts/reducer.ts index 2b44b62..fd503b4 100644 --- a/src/contexts/contracts/reducer.ts +++ b/src/contexts/contracts/reducer.ts @@ -6,6 +6,7 @@ import type { TokenContract } from '@aztec/noir-contracts.js/Token'; import type { AMMContract } from '../../../contracts/target/AMM'; import type { ProofOfPasswordContract } from '../../../contracts/target/ProofOfPassword'; +import type { SubscriptionFPC } from '@gregojuice/contracts/subscription-fpc'; import { createReducerHook, type ActionsFrom } from '../utils'; // ============================================================================= @@ -17,6 +18,7 @@ export interface Contracts { gregoCoinPremium: TokenContract | null; amm: AMMContract | null; pop: ProofOfPasswordContract | null; + fpc: SubscriptionFPC | null; } export type ContractRegistrationStage = 'base' | 'drip'; @@ -32,6 +34,7 @@ export const initialContractsState: ContractsState = { gregoCoinPremium: null, amm: null, pop: null, + fpc: null, }, isLoading: true, }; diff --git a/src/contexts/swap/SwapContext.tsx b/src/contexts/swap/SwapContext.tsx index 3439049..647f89b 100644 --- a/src/contexts/swap/SwapContext.tsx +++ b/src/contexts/swap/SwapContext.tsx @@ -214,7 +214,7 @@ export function SwapProvider({ children }: SwapProviderProps) { } } }, - [state.exchangeRate, actions] + [state.exchangeRate, actions], ); const setToAmount = useCallback( @@ -230,7 +230,7 @@ export function SwapProvider({ children }: SwapProviderProps) { } } }, - [state.exchangeRate, actions] + [state.exchangeRate, actions], ); // Computed values diff --git a/src/hooks/useSubscriptionStatus.ts b/src/hooks/useSubscriptionStatus.ts index 75cdf45..b193a21 100644 --- a/src/hooks/useSubscriptionStatus.ts +++ b/src/hooks/useSubscriptionStatus.ts @@ -6,9 +6,9 @@ import { useContracts } from '../contexts/contracts'; import { useNetwork } from '../contexts/network'; import { useOnboarding } from '../contexts/onboarding'; -export function useSubscriptionStatus(isSwapping: boolean, swapPhase: string, dripPhase: string): SubscriptionStatus { - const { wallet, currentAddress } = useWallet(); - const { getAmm } = useContracts(); +export function useSubscriptionStatus(swapPhase: string, dripPhase: string): SubscriptionStatus { + const { currentAddress } = useWallet(); + const { getAmm, getFpc } = useContracts(); const { activeNetwork } = useNetwork(); const { status: onboardingStatus } = useOnboarding(); const [status, setStatus] = useState({ kind: 'no_fpc' }); @@ -25,7 +25,8 @@ export function useSubscriptionStatus(isSwapping: boolean, swapPhase: string, dr const fetchStatus = useCallback(async () => { const amm = getAmm(); - if (!wallet || !currentAddress || !amm || !isOnboarded) return; + const fpc = getFpc(); + if (!currentAddress || !amm || !isOnboarded) return; if (!activeNetwork.subscriptionFPC) { setStatus({ kind: 'no_fpc' }); return; @@ -33,14 +34,14 @@ export function useSubscriptionStatus(isSwapping: boolean, swapPhase: string, dr if (isFetchingRef.current) return; isFetchingRef.current = true; try { - const result = await querySubscriptionStatus(wallet, activeNetwork, amm, currentAddress); + const result = await querySubscriptionStatus(activeNetwork, amm, currentAddress, fpc); setStatus(result); } catch { // Leave previous status on transient error to avoid flicker } finally { isFetchingRef.current = false; } - }, [wallet, currentAddress, activeNetwork, getAmm, isOnboarded]); + }, [currentAddress, activeNetwork, getAmm, getFpc, isOnboarded]); // Fetch after onboarding completes useEffect(() => { diff --git a/src/main.tsx b/src/main.tsx index c8cb4b1..8c4429b 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,24 +1,30 @@ -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; -import { App } from './App.tsx'; -import { NetworkProvider } from './contexts/network/NetworkContext'; -import { WalletProvider } from './contexts/wallet/WalletContext'; -import { ContractsProvider } from './contexts/contracts/ContractsContext'; -import { SwapProvider } from './contexts/swap/SwapContext'; -import { OnboardingProvider } from './contexts/onboarding/OnboardingContext'; +// Entry bootstrap. +// +// In dev we load zone.js BEFORE any other module so it can monkey-patch +// Promise/setTimeout/fetch globally. Static ESM imports are hoisted above +// any executable code in a module, so we can't put the zone.js load and +// the rest of the app in the same file — the rest would hoist above zone.js. +// +// Instead, this file conditionally imports zone.js and then dynamically +// imports the real entry. That guarantees zone.js initializes before any +// of the app's module graph starts evaluating. +// +// Zone.js is dev-only: V8's "fast await" optimization bypasses user-space +// Promise.prototype.then, breaking zone propagation unless async/await is +// transpiled (vite.config.ts sets esbuild/SWC target to es2016 in dev). +// Prod runs with esnext and no profiler — zone.js isn't included. -createRoot(document.getElementById('root')!).render( - - - - - - - - - - - - - , -); +async function boot() { + if (import.meta.env.DEV) { + await import('zone.js'); + const Zone = (globalThis as any).Zone; + // eslint-disable-next-line no-console + console.info( + '[profiler] zone.js loaded:', + Zone && Zone.current ? `ok (root zone: ${Zone.current.name})` : 'FAILED', + ); + } + await import('./app-entry'); +} + +void boot(); diff --git a/src/profiling/context.ts b/src/profiling/context.ts new file mode 100644 index 0000000..88557e4 --- /dev/null +++ b/src/profiling/context.ts @@ -0,0 +1,95 @@ +/** + * Async-context tracking via zone.js. + * + * When a profiled method runs, it forks a child zone tagged with its span ID. + * Zone.js monkey-patches Promise, setTimeout, fetch, setInterval, and other + * async APIs to propagate the zone across await boundaries and callbacks. + * + * Zone.js is loaded conditionally at app entry when `?profile` is in the URL + * (see main.tsx). When not loaded, `Zone` is undefined and every function + * here degrades to a no-op — the profiler falls back to parentless spans. + */ + +import type { Category } from './types'; + +export interface SpanContext { + id: string; + parentId: string | null; + name: string; + category: Category; +} + +const SPAN_KEY = 'profiler:span'; + +/** Access zone.js through globalThis so bundlers don't tree-shake it away. */ +function getZone(): any { + return (globalThis as any).Zone; +} + +/** True when zone.js has been loaded (profiling active). */ +export function zoneAvailable(): boolean { + const Zone = getZone(); + return !!Zone && !!Zone.current; +} + +/** Read the span context attached to the current zone (or ancestors). */ +export function currentSpan(): SpanContext | undefined { + const Zone = getZone(); + if (!Zone || !Zone.current) return undefined; + return Zone.current.get(SPAN_KEY); +} + +/** + * Walk up the zone chain and return the first span whose category is not in + * `skipCategories`. Used by the fetch interceptor to skip past node-layer + * spans so a batched RPC fetch becomes a sibling of the node calls it groups + * (rather than nested under whichever node call happened to schedule the + * batch's setTimeout first). + */ +export function findAncestorSpan(skipCategories: ReadonlySet): SpanContext | undefined { + const Zone = getZone(); + if (!Zone || !Zone.current) return undefined; + let zone = Zone.current; + while (zone) { + const span: SpanContext | undefined = zone.get(SPAN_KEY); + if (span && !skipCategories.has(span.category)) return span; + zone = zone.parent; + } + return undefined; +} + +/** + * Run `fn` inside a child zone carrying the given span as context. + * Any async operation started by `fn` (or its descendants) will inherit + * this zone and see the span via `currentSpan()`. + */ +export function runInSpan(span: SpanContext, fn: () => T): T { + const Zone = getZone(); + if (!Zone || !Zone.current) return fn(); + const zone = Zone.current.fork({ + name: `${span.category}:${span.name}`, + properties: { [SPAN_KEY]: span }, + }); + return zone.run(fn); +} + +/** + * Wrap a callback so that when it's later invoked (e.g. dequeued by a + * background worker), it runs in the zone that was current at the time + * this wrap happened. + * + * Zone.js propagates context automatically through Promise.then, setTimeout, + * setInterval, and addEventListener. But custom queue patterns (like the + * PXE's SerialQueue worker that was started once at init time in the root + * zone and processes items in-place) lose the context. This helper lets us + * manually rescue it by capturing the zone at enqueue time. + */ +export function bindCurrentZone any>(fn: F): F { + const Zone = getZone(); + if (!Zone || !Zone.current) return fn; + const captured = Zone.current; + const bound: any = (...args: any[]) => captured.run(() => fn(...args)); + bound.__zoneBound = true; + return bound as F; +} + diff --git a/src/profiling/index.ts b/src/profiling/index.ts new file mode 100644 index 0000000..819ee28 --- /dev/null +++ b/src/profiling/index.ts @@ -0,0 +1,356 @@ +/** + * Profiling orchestrator. + * + * Instruments the embedded wallet, PXE, node client, fetch, and WASM from + * the outside — no wallet code changes needed. + * + * Parent attribution uses async-context propagation via zone.js (see + * `context.ts`). Every span carries an explicit `parentId` based on the + * actual causal chain, so concurrent async operations never get confused + * with nested ones — no timing heuristics. + * + * Usage: + * await profiler.install(); // global interceptors + * profiler.instrumentWallet(wallet); // wrap wallet + its PXE + node + * profiler.start('sendTx'); + * // ... perform operation ... + * const report = profiler.stop(); + */ + +import { + installFetchInterceptor, + installWasmInterceptor, + installSimulatorInterceptorFromPXE, +} from './interceptors'; +import { currentSpan, runInSpan, zoneAvailable, bindCurrentZone, type SpanContext } from './context'; +import type { Category, ProfileRecord, ProfileReport } from './types'; + +export type { Category, ProfileRecord, ProfileReport } from './types'; + +// ─── Method wrapping ───────────────────────────────────────────────────────── + +// Methods to skip — internal plumbing, getters, or things that break if wrapped. +const SKIP = new Set([ + // JS fundamentals + 'constructor', 'toString', 'toJSON', 'valueOf', 'hasOwnProperty', + 'isPrototypeOf', 'propertyIsEnumerable', + 'then', // wrapping 'then' would break Promise detection + 'catch', 'finally', + // Logging + 'log', 'warn', 'error', 'debug', 'info', 'verbose', 'trace', + // Lifecycle (often called during init, not during profiled operations) + 'dispose', 'destroy', + // Event emitter + 'on', 'off', 'once', 'emit', 'addListener', 'removeListener', + 'addEventListener', 'removeEventListener', +]); + +/** + * Collect all method names from an object and its prototype chain, + * stopping at Object.prototype. + */ +function collectMethods(target: any): string[] { + const seen = new Set(); + let obj = target; + while (obj && obj !== Object.prototype) { + for (const name of Object.getOwnPropertyNames(obj)) { + if (SKIP.has(name) || name.startsWith('_')) continue; + try { + if (typeof obj[name] === 'function' && !seen.has(name)) { + seen.add(name); + } + } catch { + // getter that throws — skip + } + } + obj = Object.getPrototypeOf(obj); + } + return [...seen]; +} + +function wrapAllMethods( + target: any, + category: Category, + profiler: Profiler, +): () => void { + const restores: (() => void)[] = []; + const methods = collectMethods(target); + const wrappedNames: string[] = []; + + for (const name of methods) { + const original = target[name]; + if (typeof original !== 'function' || (original as any).__profiled) continue; + + const wrapped = function (this: any, ...args: any[]) { + if (!profiler.isRecording) return original.apply(this, args); + // Bind any callback-like arguments to the current zone so that custom + // queues / schedulers that call them later don't lose async context. + // (Zone.js handles Promise/setTimeout/addEventListener natively, but + // user-space callback patterns like SerialQueue.put need this.) + const boundArgs = args.map(a => + typeof a === 'function' && !(a as any).__zoneBound ? bindCurrentZone(a) : a, + ); + return profiler.runSpan(name, category, () => original.apply(this, boundArgs)); + }; + (wrapped as any).__profiled = true; + try { + target[name] = wrapped; + wrappedNames.push(name); + restores.push(() => { target[name] = original; }); + } catch (e) { + console.warn(`[profiler] Could not wrap ${category}.${name}:`, e); + } + } + + console.info(`[profiler] wrapped ${category} (${wrappedNames.length} methods):`, wrappedNames.slice(0, 10).join(', ') + (wrappedNames.length > 10 ? ', ...' : '')); + return () => restores.forEach(r => r()); +} + +/** Detect queue-like objects whose `get`/`put`/`process` are worker-loop + * infrastructure, not application operations. Their blocking `get()` can + * span seconds of idle time and pollute the profile. */ +function isQueueLike(obj: any): boolean { + try { + return typeof obj.get === 'function' && typeof obj.put === 'function'; + } catch { + return false; + } +} + +// ─── Profiler ──────────────────────────────────────────────────────────────── + +class Profiler { + private _recording = false; + private _origin = 0; + private _startedAt = 0; + private _name = ''; + private _records: ProfileRecord[] = []; + private _cleanups: (() => void)[] = []; + private _installed = false; + private _installPromise: Promise | undefined; + private _instrumentedWallets = new WeakSet(); + /** Generation counter — incremented on each start() so leaked zones from a + * previous recording can't pollute the current one, and spans that started + * during the current recording can still finalize after stop(). */ + private _generation = 0; + + get isRecording() { return this._recording; } + get isInstalled() { return this._installed; } + + /** + * Push a completed record. Called by interceptors and method wrappers. + * Accepts records from the given generation even after stop(), so spans + * whose promise resolves after stop() still get their duration recorded. + */ + record( + generation: number, + id: string, + parentId: string | null, + name: string, + category: Category, + startAbsolute: number, + duration: number, + detail?: string, + error?: boolean, + ) { + if (generation !== this._generation) return; + this._records.push({ + id, + parentId, + name, + category, + start: startAbsolute - this._origin, + duration, + detail, + error, + }); + } + + /** + * Run `fn` as a profiled span. Enters a new zone carrying the span + * context so any nested async operations can discover this span as + * their parent via `currentSpan()`. + * + * @param parentOverride - If provided, used as the span's parent instead + * of whatever `currentSpan()` returns. The new zone is still forked + * from `Zone.current` (so downstream callbacks in it see OUR new span), + * only the recorded `parentId` is changed. Useful for re-parenting + * batched fetches out from under the node call that happened to schedule + * the batch's setTimeout. + */ + runSpan( + name: string, + category: Category, + fn: () => T | Promise, + detail?: string, + parentOverride?: SpanContext | null, + ): T | Promise { + if (!this._recording) return fn(); + + const generation = this._generation; + const parent = parentOverride !== undefined ? parentOverride : currentSpan(); + const span: SpanContext = { + id: crypto.randomUUID(), + parentId: parent?.id ?? null, + name, + category, + }; + const t0 = performance.now(); + + const finalize = (error?: boolean) => { + this.record(generation, span.id, span.parentId, name, category, t0, performance.now() - t0, detail, error); + }; + + return runInSpan(span, () => { + let result: T | Promise; + try { + result = fn(); + } catch (e) { + finalize(true); + throw e; + } + if (result && typeof (result as any).then === 'function') { + return (result as Promise).then( + v => { finalize(); return v; }, + e => { finalize(true); throw e; }, + ); + } + finalize(); + return result; + }); + } + + /** Install global interceptors (fetch, bb.js WASM, standalone fns). Call once before wallet creation. */ + async install() { + if (this._installed) return this._installPromise; + // Set the flag BEFORE any await to prevent concurrent double-install. + this._installed = true; + this._installPromise = (async () => { + this._cleanups.push(installFetchInterceptor(this)); + this._cleanups.push(await installWasmInterceptor(this)); + })(); + return this._installPromise; + } + + /** + * Manually instrument a code block. Convenience wrapper around `runSpan`. + * @example + * await profiler.span('myOperation', 'wallet', async () => { ... }); + */ + span(name: string, category: Category, fn: () => T | Promise): T | Promise { + return this.runSpan(name, category, fn); + } + + /** Wrap a wallet instance + its internal PXE + node + PXE stores. Call once per wallet. */ + instrumentWallet(wallet: any) { + if (this._instrumentedWallets.has(wallet)) return; + this._instrumentedWallets.add(wallet); + + const wrapped = new Set(); + + wrapped.add(wallet); + this._cleanups.push(wrapAllMethods(wallet, 'wallet', this)); + + const node = wallet.aztecNode; + if (node) { + wrapped.add(node); + this._cleanups.push(wrapAllMethods(node, 'node', this)); + } + + const pxe = wallet.pxe; + if (pxe) { + wrapped.add(pxe); + this._cleanups.push(wrapAllMethods(pxe, 'pxe', this)); + + if (pxe.simulator) wrapped.add(pxe.simulator); + this._cleanups.push(installSimulatorInterceptorFromPXE(pxe, this)); + + this.instrumentInternals(pxe, wrapped, 3); + } + } + + /** + * Walk an object's properties and wrap methods on sub-objects. + * Recurses up to `depth` levels (default 2) to catch nested objects + * like `jobCoordinator.kvStore` whose `transactionAsync` needs its + * callback arg zone-bound for proper context propagation. + * + * Queue-like objects (BaseMemoryQueue, FifoQueue, etc.) are skipped: + * their `get`/`put`/`process` methods are worker-loop infrastructure + * that blocks for seconds waiting for items, not application operations. + */ + private instrumentInternals(root: any, alreadyWrapped: Set, depth = 2) { + if (depth <= 0) return; + for (const key of Object.getOwnPropertyNames(root)) { + if (key.startsWith('_') || key === 'log') continue; + let value: any; + try { value = root[key]; } catch { continue; } + if (!value || typeof value !== 'object' || alreadyWrapped.has(value)) continue; + + const methods = collectMethods(value); + if (methods.length === 0) continue; + + // Skip queue-like objects — they have `get`+`put` (or `process`) + // and their blocking `get()` can span seconds of idle time. + if (isQueueLike(value)) { + alreadyWrapped.add(value); + continue; + } + + alreadyWrapped.add(value); + this._cleanups.push(wrapAllMethods(value, 'store', this)); + this.instrumentInternals(value, alreadyWrapped, depth - 1); + } + } + + start(name = 'profile') { + if (this._recording) return; + this._name = name; + this._origin = performance.now(); + this._startedAt = Date.now(); + this._records = []; + this._generation++; + this._recording = true; + console.info( + `[profiler] Started: "${name}" — zone tracking: ${zoneAvailable() ? 'on' : 'OFF (every span will be a root)'}`, + ); + } + + stop(): ProfileReport { + if (!this._recording) { + return { name: '', startedAt: 0, durationMs: 0, records: [] }; + } + this._recording = false; + const durationMs = performance.now() - this._origin; + const report: ProfileReport = { + name: this._name, + startedAt: this._startedAt, + durationMs, + records: [...this._records], + }; + console.info( + `[profiler] Stopped: "${this._name}" — ${(durationMs / 1000).toFixed(2)}s, ` + + `${report.records.length} spans`, + ); + return report; + } + + download(report: ProfileReport) { + const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `profile-${report.name}-${new Date(report.startedAt).toISOString().replace(/[:.]/g, '-')}.json`; + a.click(); + URL.revokeObjectURL(url); + } + + uninstall() { + this._cleanups.forEach(c => c()); + this._cleanups = []; + this._installed = false; + } +} + +export const profiler = new Profiler(); +export type { Profiler }; diff --git a/src/profiling/interceptors.ts b/src/profiling/interceptors.ts new file mode 100644 index 0000000..1265d62 --- /dev/null +++ b/src/profiling/interceptors.ts @@ -0,0 +1,293 @@ +/** + * Fetch + WASM + simulator + standalone-function interception for profiling. + * + * All interceptors take the `Profiler` instance directly and use its + * `runSpan` method to wrap operations. That routes the work through + * `runInSpan` (zone.js), so async context propagates and every captured + * span carries the correct `parentId`. + */ + +import type { Profiler } from './index'; +import { findAncestorSpan } from './context'; + +// Categories to skip when re-parenting batched fetches. A batched RPC call +// is triggered by a setTimeout scheduled inside a `node` method, so it ends +// up nested under that first node call. Skipping `node` makes the batch +// record a sibling of the node calls it groups. +const SKIP_FOR_BATCH_PARENT = new Set(['node' as const, 'rpc' as const]); + +// ─── Fetch interceptor ────────────────────────────────────────────────────── + +export function installFetchInterceptor(profiler: Profiler): () => void { + const original = window.fetch.bind(window); + + window.fetch = ( + input: RequestInfo | URL, + init?: RequestInit, + ): Promise => { + if (!profiler.isRecording) return original(input, init); + + // Only instrument JSON-RPC POST requests. Skip WASM binary downloads, + // static assets, etc. — these pollute the profile with huge blobs. + if (!init?.body || typeof init.body !== 'string') return original(input, init); + + let method = ''; + let batched = false; + try { + const parsed = JSON.parse(init.body); + if (Array.isArray(parsed)) { + batched = true; + method = parsed.map((r: any) => r?.method ?? '?').join(', '); + } else if (parsed?.method) { + method = parsed.method; + } + } catch { + return original(input, init); + } + if (!method) return original(input, init); + + const label = batched ? `[batch] ${method}` : method; + + // For batched fetches, re-parent above any node ancestors so the batch + // is a sibling of the node calls it bundled (rather than nested under + // the first one, which is where setTimeout happened to land). + const parentOverride = batched + ? findAncestorSpan(SKIP_FOR_BATCH_PARENT) ?? null + : undefined; + + return profiler.runSpan(label, 'rpc', async () => { + return await original(input, init); + }, undefined, parentOverride) as Promise; + }; + + return () => { + window.fetch = original; + }; +} + +// ─── Msgpack operation name decoder ───────────────────────────────────────── +// bb.js backend.call receives msgpack-encoded [["OperationName", ...args]]. +// We extract just the operation name from the first few bytes. + +function decodeMsgpackOpName(buf: Uint8Array): string | null { + try { + let pos = 0; + const u8 = (o: number) => buf[o]; + + // Outer fixarray header (0x90..0x9f) + const outer = u8(pos++); + if ((outer & 0xf0) !== 0x90) return null; + // Inner fixarray header + const inner = u8(pos++); + if ((inner & 0xf0) !== 0x90) return null; + // String header + const strHdr = u8(pos++); + let strLen: number; + if ((strHdr & 0xe0) === 0xa0) { + strLen = strHdr & 0x1f; // fixstr + } else if (strHdr === 0xd9) { + strLen = u8(pos++); // str 8 + } else { + return null; + } + let name = ''; + for (let i = 0; i < strLen && pos < buf.length; i++) { + name += String.fromCharCode(u8(pos++)); + } + return name || null; + } catch { + return null; + } +} + +// ─── WASM interceptor ─────────────────────────────────────────────────────── + +function wrapBackendCall( + backend: any, + profiler: Profiler, + isSync: boolean, +): () => void { + if (!backend || typeof backend.call !== 'function' || backend.call.__profiled) + return () => {}; + + const original = backend.call.bind(backend); + + if (isSync) { + backend.call = function (inputBuffer: Uint8Array) { + if (!profiler.isRecording) return original(inputBuffer); + const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_sync'; + return profiler.runSpan(opName, 'wasm', () => original(inputBuffer)); + }; + } else { + backend.call = function (inputBuffer: Uint8Array) { + if (!profiler.isRecording) return original(inputBuffer); + const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_async'; + return profiler.runSpan(opName, 'wasm', () => original(inputBuffer)); + }; + } + + backend.call.__profiled = true; + return () => { + backend.call = original; + }; +} + +export async function installWasmInterceptor(profiler: Profiler): Promise<() => void> { + const restores: (() => void)[] = []; + + try { + const bbMod = await import('@aztec/bb.js'); + const BB = (bbMod as any).Barretenberg; + const BBSync = (bbMod as any).BarretenbergSync; + + // Patch BarretenbergSync (main-thread hashing: poseidon, pedersen, etc.) + if (BBSync) { + try { + const existing = BBSync.getSingleton(); + if (existing?.backend) + restores.push(wrapBackendCall(existing.backend, profiler, true)); + } catch { + /* not yet init'd */ + } + + if (BBSync.initSingleton && !BBSync.initSingleton.__profiled) { + const orig = BBSync.initSingleton.bind(BBSync); + BBSync.initSingleton = async (...args: any[]) => { + const inst = await orig(...args); + if (inst?.backend) + restores.push(wrapBackendCall(inst.backend, profiler, true)); + return inst; + }; + BBSync.initSingleton.__profiled = true; + restores.push(() => { + BBSync.initSingleton = orig; + }); + } + } + + // Patch Barretenberg (async — proving worker, less important but still useful) + if (BB) { + try { + const existing = BB.getSingleton(); + if (existing?.backend) + restores.push(wrapBackendCall(existing.backend, profiler, false)); + } catch { + /* not yet init'd */ + } + + if (BB.initSingleton && !BB.initSingleton.__profiled) { + const orig = BB.initSingleton.bind(BB); + BB.initSingleton = async (...args: any[]) => { + const inst = await orig(...args); + if (inst?.backend) + restores.push(wrapBackendCall(inst.backend, profiler, false)); + return inst; + }; + BB.initSingleton.__profiled = true; + restores.push(() => { + BB.initSingleton = orig; + }); + } + } + } catch { + // @aztec/bb.js not available — no WASM profiling + } + + return () => restores.forEach((r) => r()); +} + +// ─── Simulator + oracle callback interceptor ─────────────────────────────── + +/** + * Wrap every method on an ACIRCallback (oracle) object with profiling. + * Each key is an oracle function name (getNotes, getPublicDataTreeWitness, ...). + * + * We return a NEW object with wrapped methods — the original callback is + * left untouched (the ACVM only sees our wrapped version). + */ +function wrapOracleCallback(callback: any, profiler: Profiler): any { + if (!callback || typeof callback !== 'object') return callback; + + const wrapped: any = {}; + for (const key of Object.keys(callback)) { + const original = callback[key]; + if (typeof original !== 'function') { + wrapped[key] = original; + continue; + } + wrapped[key] = function (...args: any[]) { + if (!profiler.isRecording) return original.apply(this, args); + return profiler.runSpan(key, 'oracle', () => original.apply(this, args)); + }; + } + return wrapped; +} + +/** + * Patch circuit simulator prototypes by reaching through the PXE instance. + * This avoids importing @aztec/simulator or @aztec/pxe/server (which have + * native Node.js deps that break browser builds). + * + * Patches: + * - executeUserCircuit: records the circuit execution + wraps the oracle + * callback so every oracle call gets its own span. + * - executeProtocolCircuit: records protocol circuit execution. + */ +export function installSimulatorInterceptorFromPXE( + pxe: any, + profiler: Profiler, +): () => void { + const restores: (() => void)[] = []; + + const sim = pxe?.simulator; + if (!sim) return () => {}; + + const simProto = Object.getPrototypeOf(sim); + if (!simProto) return () => {}; + + if (typeof simProto.executeUserCircuit === 'function' && !simProto.executeUserCircuit.__profiled) { + const original = simProto.executeUserCircuit; + simProto.executeUserCircuit = function ( + this: any, input: any, artifact: any, callback: any, ...rest: any[] + ) { + if (!profiler.isRecording) return original.call(this, input, artifact, callback, ...rest); + const name = artifact?.name ?? artifact?.functionName ?? 'circuit'; + const contract = artifact?.contractName ?? ''; + const label = contract ? `${contract}:${name}` : name; + const wrappedCallback = wrapOracleCallback(callback, profiler); + return profiler.runSpan(label, 'sim', () => + original.call(this, input, artifact, wrappedCallback, ...rest), + ); + }; + simProto.executeUserCircuit.__profiled = true; + restores.push(() => { simProto.executeUserCircuit = original; }); + } + + if (typeof simProto.executeProtocolCircuit === 'function' && !simProto.executeProtocolCircuit.__profiled) { + const original = simProto.executeProtocolCircuit; + simProto.executeProtocolCircuit = function ( + this: any, input: any, artifact: any, callback: any, ...rest: any[] + ) { + if (!profiler.isRecording) return original.call(this, input, artifact, callback, ...rest); + const label = artifact?.name ?? 'protocol_circuit'; + const wrappedCallback = callback && typeof callback === 'object' + ? wrapOracleCallback(callback, profiler) + : callback; + return profiler.runSpan(label, 'sim', () => + original.call(this, input, artifact, wrappedCallback, ...rest), + ); + }; + simProto.executeProtocolCircuit.__profiled = true; + restores.push(() => { simProto.executeProtocolCircuit = original; }); + } + + return () => restores.forEach((r) => r()); +} + +// Note: standalone functions imported via `import { foo } from 'bar'` +// (e.g. `simulateViaNode`, `waitForTx`) aren't captured. ESM imports are +// live bindings to the exporter's local variable, not the namespace object, +// so runtime monkey-patching of the namespace doesn't affect existing +// imports in other modules. The work these functions do is still visible +// via the interceptors that capture their internals (RPC/fetch for +// `simulateViaNode`, `node.getTxReceipt` polls for `waitForTx`). diff --git a/src/profiling/types.ts b/src/profiling/types.ts new file mode 100644 index 0000000..3f57a00 --- /dev/null +++ b/src/profiling/types.ts @@ -0,0 +1,34 @@ +/** Shared profiling types. */ + +export type Category = + | 'wallet' + | 'pxe' + | 'sim' + | 'oracle' + | 'store' + | 'node' + | 'rpc' + | 'wasm'; + +export interface ProfileRecord { + /** Unique span id. Always present. */ + id: string; + /** Parent span id from async context, or null for root spans. */ + parentId: string | null; + name: string; + category: Category; + /** ms from recording origin. */ + start: number; + /** ms duration. */ + duration: number; + detail?: string; + error?: boolean; +} + +export interface ProfileReport { + name: string; + /** Date.now() at recording start. */ + startedAt: number; + durationMs: number; + records: ProfileRecord[]; +} diff --git a/src/services/contractService.ts b/src/services/contractService.ts index 402e2ee..8d7fc7d 100644 --- a/src/services/contractService.ts +++ b/src/services/contractService.ts @@ -3,7 +3,7 @@ * Pure functions for contract-related operations */ -import type { Wallet } from '@aztec/aztec.js/wallet'; +import type { BatchedMethod, Wallet, TxSimulationResultWithAppOffset } from '@aztec/aztec.js/wallet'; import type { AztecNode } from '@aztec/aztec.js/node'; import { AztecAddress } from '@aztec/aztec.js/addresses'; import { AztecAddress as AztecAddressClass } from '@aztec/aztec.js/addresses'; @@ -11,10 +11,14 @@ import { Fr } from '@aztec/aztec.js/fields'; import { FunctionSelector } from '@aztec/aztec.js/abi'; import { BatchCall, getContractInstanceFromInstantiationParams } from '@aztec/aztec.js/contracts'; import { poseidon2Hash } from '@aztec/foundation/crypto/poseidon'; +import { type FunctionCall, decodeFromAbi } from '@aztec/stdlib/abi'; +import { ExecutionPayload } from '@aztec/stdlib/tx'; +import { UtilityExecutionResult } from '@aztec/stdlib/tx'; import type { TxReceipt } from '@aztec/stdlib/tx'; import type { TokenContract } from '@aztec/noir-contracts.js/Token'; import type { AMMContract } from '../../contracts/target/AMM'; import type { ProofOfPasswordContract } from '../../contracts/target/ProofOfPassword'; +import { SubscriptionFPC } from '@gregojuice/contracts/subscription-fpc'; import { BigDecimal } from '../utils/bigDecimal'; import type { NetworkConfig } from '../config/networks'; import type { OnboardingResult } from '../contexts/onboarding/reducer'; @@ -26,6 +30,7 @@ export interface SwapContracts { gregoCoin: TokenContract; gregoCoinPremium: TokenContract; amm: AMMContract; + fpc: SubscriptionFPC | null; } /** @@ -33,6 +38,7 @@ export interface SwapContracts { */ export interface DripContracts { pop: ProofOfPasswordContract; + fpc: SubscriptionFPC | null; } /** @@ -138,7 +144,10 @@ export async function registerSwapContracts( const gregoCoinPremium = TokenContract.at(gregoCoinPremiumAddress, wallet); const amm = AMMContract.at(ammAddress, wallet); - return { gregoCoin, gregoCoinPremium, amm }; + // Instantiate FPC wrapper if configured + const fpc = subFPC && fpcAddress ? SubscriptionFPC.at(fpcAddress, wallet) : null; + + return { gregoCoin, gregoCoinPremium, amm, fpc }; } /** @@ -206,7 +215,11 @@ export async function registerDripContracts( // Instantiate the ProofOfPassword contract const pop = ProofOfPasswordContract.at(popAddress, wallet); - return { pop }; + // Instantiate FPC wrapper if configured + const fpcAddr = subFPC ? AztecAddressClass.fromString(subFPC.address) : undefined; + const fpc = fpcAddr ? SubscriptionFPC.at(fpcAddr, wallet) : null; + + return { pop, fpc }; } /** @@ -339,11 +352,11 @@ function markSubscribed(fpcAddress: string, configIndex: number, userAddress: st * Uses subscribe on first call, sponsor on subsequent calls. */ export async function executeSponsoredSwap( - wallet: Wallet, network: NetworkConfig, amm: SwapContracts['amm'], gregoCoin: SwapContracts['gregoCoin'], gregoCoinPremium: SwapContracts['gregoCoinPremium'], + fpc: SubscriptionFPC, userAddress: AztecAddress, amountOut: number, amountInMax: number, @@ -372,12 +385,6 @@ export async function executeSponsoredSwap( ); } - const fpcAddress = AztecAddressClass.fromString(subFPC.address); - const { SubscriptionFPCContract } = await import('@gregojuice/contracts/artifacts/SubscriptionFPC'); - const { SubscriptionFPC } = await import('@gregojuice/contracts/subscription-fpc'); - const rawFPC = SubscriptionFPCContract.at(fpcAddress, wallet); - const fpc = new SubscriptionFPC(rawFPC); - const subscribed = hasSubscription(subFPC.address, configIndex, userAddress.toString()); if (subscribed) { @@ -422,12 +429,12 @@ export async function executeUnsponsoredSwap( } export type SubscriptionStatusKind = - | 'loading' // query in flight - | 'no_fpc' // no FPC configured for this network — hide everything - | 'sponsored' // user not yet subscribed, slots available — first swap will be free - | 'active' // user has subscription with uses remaining — swap is free - | 'full' // no slots left, user never subscribed — must bridge - | 'depleted'; // user's uses exhausted — must bridge + | 'loading' // query in flight + | 'no_fpc' // no FPC configured for this network — hide everything + | 'sponsored' // user not yet subscribed, slots available — first swap will be free + | 'active' // user has subscription with uses remaining — swap is free + | 'full' // no slots left, user never subscribed — must bridge + | 'depleted'; // user's uses exhausted — must bridge export interface SubscriptionStatus { kind: SubscriptionStatusKind; @@ -440,13 +447,13 @@ export interface SubscriptionStatus { * Returns the status kind based on available slots and user subscription state. */ export async function querySubscriptionStatus( - wallet: Wallet, network: NetworkConfig, amm: SwapContracts['amm'], userAddress: AztecAddress, + fpc: SubscriptionFPC | null, ): Promise { const subFPC = network.subscriptionFPC; - if (!subFPC) return { kind: 'no_fpc' }; + if (!subFPC || !fpc) return { kind: 'no_fpc' }; // Derive configIndex + selector from the AMM's function map — take the first entry const ammFunctions = subFPC.functions[amm.address.toString()]; @@ -458,15 +465,11 @@ export async function querySubscriptionStatus( const selector = FunctionSelector.fromString(selectorHex); const configId = await poseidon2Hash([amm.address.toField(), selector.toField(), new Fr(configIndex)]); - const fpcAddress = AztecAddressClass.fromString(subFPC.address); - const { SubscriptionFPCContract } = await import('@gregojuice/contracts/artifacts/SubscriptionFPC'); - const rawFPC = SubscriptionFPCContract.at(fpcAddress, wallet); - // SlotNote is owned by the FPC — must simulate from fpc.address // SubscriptionNote is owned by the user — must simulate from userAddress const [{ result: slotsResult }, { result: subInfoResult }] = await Promise.all([ - rawFPC.methods.count_available_slots(configId).simulate({ from: fpcAddress }), - rawFPC.methods.get_subscription_info(userAddress, configId).simulate({ from: userAddress }), + fpc.methods.count_available_slots(configId).simulate({ from: fpc.address }), + fpc.methods.get_subscription_info(userAddress, configId).simulate({ from: userAddress }), ]); const availableSlots = Number(slotsResult); @@ -510,6 +513,7 @@ export async function executeDrip( wallet: Wallet, network: NetworkConfig, pop: ProofOfPasswordContract, + fpc: SubscriptionFPC, password: string, recipient: AztecAddress, ): Promise { @@ -524,12 +528,6 @@ export async function executeDrip( throw new Error(`No subscription config found for ${pop.address.toString()} selector ${call.selector.toString()}`); } - const fpcAddress = AztecAddressClass.fromString(subFPC.address); - const { SubscriptionFPCContract } = await import('@gregojuice/contracts/artifacts/SubscriptionFPC'); - const { SubscriptionFPC } = await import('@gregojuice/contracts/subscription-fpc'); - const rawFPC = SubscriptionFPCContract.at(fpcAddress, wallet); - const fpc = new SubscriptionFPC(rawFPC); - const accounts = await wallet.getAccounts(); const userAddress = accounts[0]?.item ?? recipient; diff --git a/vite.config.ts b/vite.config.ts index 2951983..a628fe6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -93,11 +93,23 @@ const chunkSizeValidator = (limits: ChunkSizeLimit[]): Plugin => { }; // https://vite.dev/config/ -export default defineConfig(({ mode }) => { +export default defineConfig(({ command, mode }) => { const env = loadEnv(mode, process.cwd(), ''); + + // Profiling (zone.js-based async context tracking) runs only in dev. + // V8's "fast await" optimization bypasses user-space Promise.prototype.then + // for native `async function` bodies, breaking zone.js propagation. By + // lowering the esbuild/SWC target to es2016 in dev, we force async/await + // to be transpiled to Promise-based state machines that DO go through + // user-level .then() — which zone.js can hook. Prod keeps esnext for speed. + const isDev = command === 'serve'; + const esTarget = isDev ? 'es2016' : 'esnext'; + return { base: './', logLevel: process.env.CI ? 'error' : undefined, + esbuild: { target: esTarget }, + build: { target: esTarget }, server: { // Headers needed for bb WASM to work in multithreaded mode headers: { @@ -111,9 +123,14 @@ export default defineConfig(({ mode }) => { optimizeDeps: { exclude: ['@aztec/noir-acvm_js', '@aztec/noir-noirc_abi', '@aztec/bb.js'], include: ['@gregojuice/embedded-wallet/ui'], + esbuildOptions: { target: esTarget }, }, plugins: [ - react({ jsxImportSource: '@emotion/react' }), + react({ + jsxImportSource: '@emotion/react', + // Match esbuild target in dev so async/await gets transpiled for zone.js. + ...(isDev ? { devTarget: 'es2016' as const } : {}), + }), nodePolyfillsFix({ include: ['buffer', 'path'] }), chunkSizeValidator([ { diff --git a/yarn.lock b/yarn.lock index 4684099..e672dd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,66 +645,66 @@ __metadata: languageName: node linkType: hard -"@aztec/accounts@npm:4.2.0-nightly.20260412, @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" +"@aztec/accounts@npm:4.3.0-nightly.20260417, @aztec/accounts@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/accounts@npm:4.3.0-nightly.20260417" + dependencies: + "@aztec/aztec.js": "npm:4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260417" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/4c309f8391993139d4a5648e9f700901a5dd11fdf69676c7bea772f085314699effd33f259706814c9cedb5cd4b5065dc1f3c1ce1534e8524230c00c0c70c2ed + checksum: 10c0/bac0071c30830e05fcd3bbcc7bdf1294af3595b2c06fe40a773b7368d4240200653fcc9389370e760fa4759695487956b45143158ae01972467054b62370b88f languageName: node linkType: hard -"@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" +"@aztec/aztec.js@npm:4.3.0-nightly.20260417, @aztec/aztec.js@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/aztec.js@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260417" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" axios: "npm:^1.13.5" tslib: "npm:^2.4.0" viem: "npm:@aztec/viem@2.38.2" zod: "npm:^3.23.8" - checksum: 10c0/34bdbe90a03e5af753c56b5b2d5c0875e925676e91b043f828decf0af1381b2fe95564ef75f740d0a094fff6b76481e88dedc63a06da3096f0df310113781b21 + checksum: 10c0/ff6f61dad60f50fd295090f7ffd91a97a98bac0b4d6d0a0957ecfc579774ed297f36710588bf73cdfa86bb28cdc0d61bc69ac8666f9de71dfa55a1d3a66dce8d languageName: node linkType: hard -"@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" +"@aztec/bb-prover@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/bb-prover@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260417" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260417" + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + "@aztec/simulator": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260417" + "@aztec/world-state": "npm:4.3.0-nightly.20260417" 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/d9b2f13852acd8f15b4ce3a2d2a6147ce5f5ca7a9205ff2cb30f47895c6f726fe3690919206db8b02ace465804392432cee7a158112c4dab17bca5783bec4fc9 + checksum: 10c0/fc934eb838b89b6073e209983ccdeb42e74d357e73c49bec36e258e470e775a62f7d0210199d37002aebed2ca9078a5a864d124b4ba907b9bcc7560578c9a224 languageName: node linkType: hard -"@aztec/bb.js@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/bb.js@npm:4.2.0-nightly.20260412" +"@aztec/bb.js@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/bb.js@npm:4.3.0-nightly.20260417" 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/3e8e9a3888e414e836d8e0421136089266be9a66717238719c44da21a32e9ef176e2bc2829b0f23aa6aa0e810acc4f92ebc3b892188dec7f3bb7afe1760f2b24 + checksum: 10c0/f6ee4faa378a3772902c2d96aec0fe137531f0f1cc8b30b76972c12fc0d67e223f69e896632714c2447088dfc5c21d6840a6e0a06a8773c8b9a47fffaaa3e16a languageName: node linkType: hard -"@aztec/blob-lib@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/blob-lib@npm:4.2.0-nightly.20260412" +"@aztec/blob-lib@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/blob-lib@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/constants": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" "@crate-crypto/node-eth-kzg": "npm:^0.10.0" tslib: "npm:^2.4.0" - checksum: 10c0/fe8b19a8bb26970cae58876b705ee3101790ac10041fef2f2d6b9b9acef6ea5986316704bb187307c05bd78b59cc06147f49bf1042adead24142e6c4b631c543 + checksum: 10c0/aae5bcb04ee2d631b23bcb4bbdee6eb1ca2d280e7cec62811656d0e0ce0cd0286afd48dadff5fb06f90cb44d0834b516ac07c522c463945e229df86a9e17c756 languageName: node linkType: hard -"@aztec/builder@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/builder@npm:4.2.0-nightly.20260412" +"@aztec/builder@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/builder@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" commander: "npm:^12.1.0" - checksum: 10c0/5488207fde2ab772e360e0a8ace186917c684002da01d48fcaa03f28fd82c0ffb9e64d3d686f406d02486e0703138695d73483e4d3302fe6c8b9435ab47ab685 + checksum: 10c0/8f1adbb402a809db671027bafc54e3a0a60bae086f6a86e43ac7dbf5838e7075f18b9ffbed81798bf524ef7dc25ab1b652564ba8c59aa26e7e6293cf16cbe8e7 languageName: node linkType: hard -"@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" +"@aztec/constants@npm:4.3.0-nightly.20260417, @aztec/constants@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/constants@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/cfec60215306956d536ded2fb285ee65bed9a4062c61f76dc8cb6dc7b6efd2501091ef5df7f699f8b5a6df3493febaba781f5a8ad4cab2ca4547cca322511322 + checksum: 10c0/3589bd9a42ecd6da3a1b542f547227a3fdc9f6bc9654d3f8faecd021a14481d6be46831890dadcc6a8fe2fa1bdafa88bf90aec0844033e8899a51ca1dd7cef48 languageName: node linkType: hard -"@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" +"@aztec/entrypoints@npm:4.3.0-nightly.20260417, @aztec/entrypoints@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/entrypoints@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/12383a50331cfce063c2cd02477ff69f85bcbf9b7d81fa44e0eda6b73107819b1cd8d4bb3312926e4f6662c1f640f39c3ca5c5a07df20559a12ce23dcbbb03cf + checksum: 10c0/1b038e4d939edbb19c75902352f73016a25e915cd19929f01262a0e54e64b2da19041f553705d2c79349b5c6cbdf26e8224f8ad6c7e7ec21ff1b22284aa7b9a1 languageName: node linkType: hard -"@aztec/ethereum@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/ethereum@npm:4.2.0-nightly.20260412" +"@aztec/ethereum@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/ethereum@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/blob-lib": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260417" "@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/b7bdb1015b7a453259e5da94599c4792944c1339d14707cb0dc7811aac698dee0d8c56258cb8aad825c020bcd769935546087d29802e193e3a156b11aa7c793a + checksum: 10c0/0cf832cfb468186bb0a78384f866f60aca10a86c3d7de3509c3faa91e347fbc8fde8c051aed57969d9274b4ed31c370225f3b85421ee0f254ac72798378db194 languageName: node linkType: hard -"@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" +"@aztec/foundation@npm:4.3.0-nightly.20260417, @aztec/foundation@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/foundation@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" "@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/379f6a93893775d99b3c91793eb3df7c53d8ff6424de83ac1a5d89344c7366b0892929593f08cece3d2e56ab825727b5d9ba546788cb6ff0cc3a9d8c908034ed + checksum: 10c0/f54e7477ab56b4edca07d19a444f82511995399010895509bba124542a6399e013690c68ed26c62582241e2ea3686656b0ee51b38c5e6ee8ef579c83516491fa languageName: node linkType: hard -"@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" +"@aztec/key-store@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/key-store@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/kv-store": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/84e34402e3d7490d2a0c2c005aca57834c1b1ecbb621bd91e3a4e398cafe04617da109124757daae651ab2720e86f1f258267f9987303a10f804b5d61b37513d + checksum: 10c0/f9757cd36ba38ec4882e1ad924b1ebec21bafe29932bbd6c3946ebbe41b94a15bffa698cbfe3b34445f225138c280e92728e4714fc3b6ff2086d4b23d7519418 languageName: node linkType: hard -"@aztec/kv-store@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/kv-store@npm:4.2.0-nightly.20260412" +"@aztec/kv-store@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/kv-store@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/native": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" 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/3fdb700f0842a12c98fc32e7a54abc3405f4dac3a33a556002cc2fe9d969c8e0a326168cc611e76d137ac6f1779dcb97cb3bc27b60c3698ac6726393668c5ac9 + checksum: 10c0/72cbfd4be93452a7d6b6fb946f8803caef11d7867875f6a9a0ad075e7cbec34ec24493352ce52e4d1ef8e09d8827087eb65f605b7666e98819ed6fe12e474d22 languageName: node linkType: hard -"@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" +"@aztec/l1-artifacts@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/l1-artifacts@npm:4.3.0-nightly.20260417" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/209dd1b1e9cf42da472a866415a2c4d8191dc0e5209eec406c253f4100b7c1d3aaa28e96cf5c7fab7aab1670dd8b98a7cd3ab3631fa62ef14f0a5394652faa1c + checksum: 10c0/04f1cb4e2af273d5176562c2a86a336f8bfa6d2bef1591d11a2b39bdf582b4b45142e791cd5a8fe3f1153e85140729f830b30aecd16d85f777d96a198482b091 languageName: node linkType: hard -"@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" +"@aztec/merkle-tree@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/merkle-tree@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/kv-store": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" sha256: "npm:^0.2.0" tslib: "npm:^2.4.0" - checksum: 10c0/f8c6c0fae86589d216f38780d46caceb678f8f9a6e3ace3a43eff00f9840246b8712854d0c126233f2c5364983635bb382333e4a505f63759b4834206f3c9593 + checksum: 10c0/b7670bcb4ac9aab1dcad531aeb0cb19023d61a5082a6b5e1c187d10500e97c5acd2d02e59e26df3a552ed54c6f7efa83f8221ffdbdaf19919e3ade8bbef6a6b2 languageName: node linkType: hard -"@aztec/native@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/native@npm:4.2.0-nightly.20260412" +"@aztec/native@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/native@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/bb.js": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" msgpackr: "npm:^1.11.2" - checksum: 10c0/32ad54727e85a4fed81707022b98940e42b15a3858c36cadbbb6429f07b98ebf9c3fc0f81bb7d3b112536f97458597f7005c04aa79a8a26d147ff38a2b469e06 + checksum: 10c0/97e5df87a21eb4670cbf7093d30f10af9db8336b2ab749bc32e5fb5a8460fe43dff6bf11745d2715e83cc1e5b632dcc399e33d7edac2710aa59ed07ef0cfe94b languageName: node linkType: hard -"@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 +"@aztec/noir-acvm_js@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-acvm_js@npm:4.3.0-nightly.20260417" + checksum: 10c0/7b67774febfc32815497bcd66df629c3bd1e8ddc0df0786268d7818b10d4d5c0074c354e36e7148c42dbd5156108c6a5a6345b3f556ba392330a29550b12a3a1 languageName: node linkType: hard -"@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" +"@aztec/noir-contracts.js@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-contracts.js@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" + "@aztec/aztec.js": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/ed8e7fdf736232dd8a6b3abac0ee6d47c68acb32305df47e916f4537b4aa52d26aa6a8de7b25d676275077114e20a4a90f9ca294280582a0828d1a359b605c84 + checksum: 10c0/49b0b41b8f83f3a73fd6fcdb0bf62efb16b3466138e620a97e24b3a1bceeeb6d939c1167a51a7c40c3208396e7f3724bcf88870647988b3a3eed7e8b0cb9053f languageName: node linkType: hard -"@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" +"@aztec/noir-noir_codegen@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-noir_codegen@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/noir-types": "npm:4.2.0-nightly.20260412" - glob: "npm:^13.0.0" + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + glob: "npm:^13.0.6" ts-command-line-args: "npm:^2.5.1" bin: noir-codegen: lib/main.js - checksum: 10c0/31b7aedfb8f8403660462ceff20ec31c3ac69b9723ea9dd52b0edb13c17ea657f9b9b2b9e64efe2009eaa9934c9bc5c711e4409a9ffe975d3a12a69209255d39 + checksum: 10c0/43622589b73c51a1ab4abb1dcd7026a9bb686e1fdb455083d5e99f34536270b73d3b61314473bc7f3d69d309c6bb10bd18bd8bde67a79acde71512a10d61cc82 languageName: node linkType: hard -"@aztec/noir-noirc_abi@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/noir-noirc_abi@npm:4.2.0-nightly.20260412" +"@aztec/noir-noirc_abi@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-noirc_abi@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/noir-types": "npm:4.2.0-nightly.20260412" - checksum: 10c0/22c0d8652e2bb0e1eea3da5942349c944c4b438b3384b28002afcd4e6c0852e1d57855ec2432a5bdbf51bcbef6514dbac2e1f3ecddfa2c7392f90769f49d1523 + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + checksum: 10c0/9575fcbc1855888e360a23cc0538ca35957d49adde874085132fc811783694ef7e4aed92b61d7de80a3355e8568f095b2268ba27920fa2662bd57028263df4a4 languageName: node linkType: hard -"@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" +"@aztec/noir-protocol-circuits-types@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-protocol-circuits-types@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/blob-lib": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260417" + "@aztec/noir-noir_codegen": "npm:4.3.0-nightly.20260417" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260417" + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" change-case: "npm:^5.4.4" tslib: "npm:^2.4.0" - checksum: 10c0/d9a21827f9c4cb57e8065db77bf745750dc80c89217e9e94eb116909be187bbd0aea0a30ce7bfcb34eb7f226e2a28902af62e94b5ed088982e27d023a53d644a + checksum: 10c0/cd02171472923ca0135424437cf7c284467e2e862cf956ec05ab1aa75fcfa7c4fe1c9dae7b33e3dd99d150dc18c92f24b5ca9fb717a272505db6f1e36e9912b4 languageName: node linkType: hard -"@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 +"@aztec/noir-types@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/noir-types@npm:4.3.0-nightly.20260417" + checksum: 10c0/5228b47699c2a5adae103b03c0fd18468d6032df7e9c366808c41d831c625aeadd7e5feb1dd0e5d30dc2b3afb8f8254ad10b02dd8778034d81df817f4173ce50 languageName: node linkType: hard -"@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" +"@aztec/protocol-contracts@npm:4.3.0-nightly.20260417, @aztec/protocol-contracts@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/protocol-contracts@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/constants": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" lodash.chunk: "npm:^4.2.0" lodash.omit: "npm:^4.5.0" tslib: "npm:^2.4.0" - 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" + checksum: 10c0/ad6902a41f15dff32874b0c5dc1b830b5d8aedfbecb65bb194c3a5034172c94ea301601eb561159a00fe186d6d441e0cb5b3a4358559e02dc259e19e44906550 + languageName: node + linkType: hard + +"@aztec/pxe@npm:4.3.0-nightly.20260417, @aztec/pxe@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/pxe@npm:4.3.0-nightly.20260417" + dependencies: + "@aztec/bb-prover": "npm:4.3.0-nightly.20260417" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" + "@aztec/builder": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/key-store": "npm:4.3.0-nightly.20260417" + "@aztec/kv-store": "npm:4.3.0-nightly.20260417" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260417" + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/simulator": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" 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/6096abce2e6c7a894a8bd1ef0dcd2a28e2b694bb9beb03e73255817d9274519eae2f3c9ee0152792bba6579f7271fd4f33fc7f589a14fbd8b3c25f51c1e7d47d + checksum: 10c0/383c73ed82b64c3dad58cddaf4b4d560e85a4edc8ce7125e9196d6bf3df4db92e990a2d0243724a6810d44e645f783bc444c9a6ee04770b1367381a62507887c languageName: node linkType: hard -"@aztec/simulator@npm:4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/simulator@npm:4.2.0-nightly.20260412" +"@aztec/simulator@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/simulator@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/native": "npm:4.3.0-nightly.20260417" + "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260417" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260417" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260417" + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260417" + "@aztec/world-state": "npm:4.3.0-nightly.20260417" lodash.clonedeep: "npm:^4.5.0" lodash.merge: "npm:^4.6.2" tslib: "npm:^2.4.0" - checksum: 10c0/6c09b23f76ea3327e23f43cccc51aff6fca52599f755180173bdd170a116e25ee28cc89bd3b88b6ba0920256b0de3ff205007d0df4b7624717c4c9f3f0dd2c0d + checksum: 10c0/e47881ec005099d0bbeeec8a5c7f1ce6b11d88d929b19fbcac816e8710b48cf4afab2d8b486c8c461373a8b8800708d68ddf2ad83846eea085438dd407cc288b languageName: node linkType: hard -"@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" +"@aztec/stdlib@npm:4.3.0-nightly.20260417, @aztec/stdlib@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/stdlib@npm:4.3.0-nightly.20260417" dependencies: "@aws-sdk/client-s3": "npm:^3.892.0" - "@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" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" + "@aztec/blob-lib": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260417" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260417" + "@aztec/validator-ha-signer": "npm:4.3.0-nightly.20260417" "@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/061dec0273812223b5631da4222f6d2c718717ee8b78846b1fb5cfe0b2fe9b6a9b642023fcfa7fa96477e28e3601d86abd387c71e09258c54c963a397ef5a2d0 + checksum: 10c0/b295d4b7e129d1b8a5e4229a287123b6c83cdf24eb5285ec5b72d22bb31669edf2cecbd2b495bede0c5574239c4dd225dac7babf3e73d00bed9acce7428d8bca languageName: node linkType: hard -"@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" +"@aztec/telemetry-client@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/telemetry-client@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/api-logs": "npm:^0.55.0" "@opentelemetry/core": "npm:^1.28.0" @@ -1058,70 +1058,70 @@ __metadata: "@opentelemetry/semantic-conventions": "npm:^1.28.0" prom-client: "npm:^15.1.3" viem: "npm:@aztec/viem@2.38.2" - checksum: 10c0/5306a910f25b45902b5cf808a3e019344e7ad6892728958883066d05a62df895cda3805d92de988680b4f3bd7e28716b0970e08084ec21a3be10f361ba09516f + checksum: 10c0/3bc43c38c89f082c93e219aa83391cf3a4230fd8d2d0c92baf3d75e0bb84b7063e045689cd493e326f10cf30a7bc567048d181245d3a89d9cc2fe80b218e7f16 languageName: node linkType: hard -"@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" +"@aztec/validator-ha-signer@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/validator-ha-signer@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/ethereum": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" node-pg-migrate: "npm:^8.0.4" pg: "npm:^8.11.3" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/0f829eafc382bcba3bd90404d4135c1b5b5aa5c9359a486e195d6271056d0bc03dfb5bde4df50c8ed1823d4155870555577c50b938cb550bc882e376f1d8a190 + checksum: 10c0/78a788cf21b052a933cf145d722edf74bc769b713b676c0dfc94481e176c7e9f398a0fccc6883255b311172be651c1cb892826d42c37863663eb5bd17ad7dade languageName: node linkType: hard -"@aztec/wallet-sdk@npm:4.2.0-nightly.20260412, @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" +"@aztec/wallet-sdk@npm:4.3.0-nightly.20260417, @aztec/wallet-sdk@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/wallet-sdk@npm:4.3.0-nightly.20260417" dependencies: - "@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 + "@aztec/aztec.js": "npm:4.3.0-nightly.20260417" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/pxe": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" + checksum: 10c0/d65e1d42261b4e0ec1faa4c879c915680b40807adef9ca3880dfc3e4e4ff44e85920c0fb63a902385585d04cc15b839d7bba3faefaf34dc9dca639bf9f986ee9 languageName: node linkType: hard -"@aztec/wallets@npm:v4.2.0-nightly.20260412": - version: 4.2.0-nightly.20260412 - resolution: "@aztec/wallets@npm:4.2.0-nightly.20260412" +"@aztec/wallets@npm:v4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/wallets@npm:4.3.0-nightly.20260417" dependencies: - "@aztec/accounts": "npm:4.2.0-nightly.20260412" - "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" - "@aztec/entrypoints": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/kv-store": "npm:4.2.0-nightly.20260412" - "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" - "@aztec/pxe": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" - "@aztec/wallet-sdk": "npm:4.2.0-nightly.20260412" - checksum: 10c0/1b697812c4119c2a00815f77da5be1fa5eaa8e9ffc7937b872c12c0970d9ff917295258e3fb83baacdb19c78cc5e9079c0f1f1b2763e22d080859df86ab4bb08 + "@aztec/accounts": "npm:4.3.0-nightly.20260417" + "@aztec/aztec.js": "npm:4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/kv-store": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/pxe": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" + "@aztec/wallet-sdk": "npm:4.3.0-nightly.20260417" + checksum: 10c0/ac474b73633b7868658542af4c6070afc28d8fc2c18069d0c9ab643d4cc856205000b0061b019e1670f2375047d22f85eeb5c0d15366254fa63b68f586856220 languageName: node linkType: hard -"@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" +"@aztec/world-state@npm:4.3.0-nightly.20260417": + version: 4.3.0-nightly.20260417 + resolution: "@aztec/world-state@npm:4.3.0-nightly.20260417" dependencies: - "@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" + "@aztec/constants": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/kv-store": "npm:4.3.0-nightly.20260417" + "@aztec/merkle-tree": "npm:4.3.0-nightly.20260417" + "@aztec/native": "npm:4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/71a72b1a8618e899fa5282a944e0d8b965635d20a3699fb124db57c13550a54afceeca7de31f6bd89e8cd5d90507f0d006e3e10ccbd1d520b3adf0e7a89c8458 + checksum: 10c0/67b469ccfea54d8531e85a448db7e0d71a475535aa9e468993fc932cc0b02b99be11c12c32926186934c1f9567101d7661f2a893c7d0f6b3dcc3b0bfdd36b292 languageName: node linkType: hard @@ -1773,30 +1773,29 @@ __metadata: languageName: node linkType: hard -"@gregojuice/contracts@npm:0.0.15, @gregojuice/contracts@npm:^0.0.15": - version: 0.0.15 - resolution: "@gregojuice/contracts@npm:0.0.15" +"@gregojuice/contracts@npm:0.0.18, @gregojuice/contracts@npm:^0.0.18": + version: 0.0.18 + resolution: "@gregojuice/contracts@npm:0.0.18" dependencies: - "@aztec/aztec.js": "npm:v4.2.0-nightly.20260412" - "@aztec/foundation": "npm:v4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:v4.2.0-nightly.20260412" - "@aztec/wallets": "npm:v4.2.0-nightly.20260412" - checksum: 10c0/780a854c9ba80c6595a1516b7a953ea44f37122b9484912bb3f6f61cf24f3abd846c2f596fb021b9f4b19737e17eca5e29a720e35d3dbccc0392f9c4e7c8de4a + "@aztec/aztec.js": "npm:v4.3.0-nightly.20260417" + "@aztec/foundation": "npm:v4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260417" + "@aztec/wallets": "npm:v4.3.0-nightly.20260417" + checksum: 10c0/63fd200596cab79275a43b19b72b89a435454ae87d8fcbabb9174e3ab30d4d6d728c4372920026dc165108a1ff3ce58cb1ea9f51b9950fba974f2b7e0baae35a languageName: node linkType: hard -"@gregojuice/embedded-wallet@npm:^0.0.15": - version: 0.0.15 - resolution: "@gregojuice/embedded-wallet@npm:0.0.15" +"@gregojuice/embedded-wallet@npm:^0.0.18": + version: 0.0.18 + resolution: "@gregojuice/embedded-wallet@npm:0.0.18" dependencies: - "@aztec/aztec.js": "npm:v4.2.0-nightly.20260412" - "@aztec/entrypoints": "npm:v4.2.0-nightly.20260412" - "@aztec/foundation": "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" - "@aztec/wallets": "npm:v4.2.0-nightly.20260412" - "@gregojuice/contracts": "npm:0.0.15" + "@aztec/aztec.js": "npm:v4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:v4.3.0-nightly.20260417" + "@aztec/foundation": "npm:v4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260417" + "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260417" + "@aztec/wallets": "npm:v4.3.0-nightly.20260417" + "@gregojuice/contracts": "npm:0.0.18" peerDependencies: "@emotion/react": ">=11" "@mui/icons-material": ">=6" @@ -1811,7 +1810,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/bf3782ac83b4ffd7726ae0ccae1a695e003a19f36fc90bd6f12d839a1dd27c49e180e8d74c64459b76a9e6d78dbbb77e5c1613f70449b98715e6163ef6f93537 + checksum: 10c0/c3c3c9a65179bb01135b21500c392fdf852d429ebd38adbe0df9d1876a5fac8c1f0df5a2ca549c403e442b85532ad262a82f726830e7a13a71eb108c2f1df030 languageName: node linkType: hard @@ -5999,7 +5998,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^13.0.0": +"glob@npm:^13.0.6": version: 13.0.6 resolution: "glob@npm:13.0.6" dependencies: @@ -6086,22 +6085,22 @@ __metadata: version: 0.0.0-use.local resolution: "gregoswap@workspace:." dependencies: - "@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/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" - "@aztec/wallets": "npm:v4.2.0-nightly.20260412" + "@aztec/accounts": "npm:v4.3.0-nightly.20260417" + "@aztec/aztec.js": "npm:v4.3.0-nightly.20260417" + "@aztec/constants": "npm:v4.3.0-nightly.20260417" + "@aztec/entrypoints": "npm:v4.3.0-nightly.20260417" + "@aztec/foundation": "npm:v4.3.0-nightly.20260417" + "@aztec/noir-contracts.js": "npm:v4.3.0-nightly.20260417" + "@aztec/protocol-contracts": "npm:v4.3.0-nightly.20260417" + "@aztec/pxe": "npm:v4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260417" + "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260417" + "@aztec/wallets": "npm:v4.3.0-nightly.20260417" "@emotion/react": "npm:^11.14.0" "@emotion/styled": "npm:^11.14.0" "@eslint/js": "npm:^9.18.0" - "@gregojuice/contracts": "npm:^0.0.15" - "@gregojuice/embedded-wallet": "npm:^0.0.15" + "@gregojuice/contracts": "npm:^0.0.18" + "@gregojuice/embedded-wallet": "npm:^0.0.18" "@mui/icons-material": "npm:^6.3.1" "@mui/material": "npm:^6.3.1" "@mui/styles": "npm:^6.3.1" @@ -6126,6 +6125,7 @@ __metadata: vite: "npm:^7.1.4" vite-plugin-node-polyfills: "npm:^0.24.0" zod: "npm:^3.23.8" + zone.js: "npm:^0.16.1" languageName: unknown linkType: soft @@ -9841,3 +9841,10 @@ __metadata: checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c languageName: node linkType: hard + +"zone.js@npm:^0.16.1": + version: 0.16.1 + resolution: "zone.js@npm:0.16.1" + checksum: 10c0/6f3638310e89697b0a21a4e7282b2505f26fb32dabb6ca1f09b721279e8512436efff199db0c7e3c7b7d11400dd42c88fa5cffc85984db56b9ac497a0050fe11 + languageName: node + linkType: hard