From 8baaa9b9df2e21b791ec65f923acb8a9aad4cc8b Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 14 Apr 2026 15:05:51 +0200 Subject: [PATCH 1/6] wip --- src/App.tsx | 12 + src/components/ProfilePanel.tsx | 1013 +++++++++++++++++++++++++++++++ src/profiling/index.ts | 255 ++++++++ src/profiling/interceptors.ts | 393 ++++++++++++ vite.config.ts | 2 + 5 files changed, 1675 insertions(+) create mode 100644 src/components/ProfilePanel.tsx create mode 100644 src/profiling/index.ts create mode 100644 src/profiling/interceptors.ts diff --git a/src/App.tsx b/src/App.tsx index 9e42f1d..d072ae2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,11 @@ 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'; + +const ProfilePanel = lazy(() => + import('./components/ProfilePanel').then(m => ({ default: m.ProfilePanel })), +); export function App() { const { disconnectWallet, setCurrentAddress, currentAddress, error: walletError, isLoading: walletLoading } = useWallet(); @@ -137,6 +142,13 @@ export function App() { {/* Transaction Progress Toasts (embedded wallet only) */} + + {/* Performance profiling panel — activate with ?profile=1 */} + {new URLSearchParams(location.search).has('profile') && ( + + + + )} ); } diff --git a/src/components/ProfilePanel.tsx b/src/components/ProfilePanel.tsx new file mode 100644 index 0000000..f2f9ecc --- /dev/null +++ b/src/components/ProfilePanel.tsx @@ -0,0 +1,1013 @@ +/** + * Profiling panel — canvas-based waterfall chart. + * Activated by ?profile query parameter. + * + * Shows every instrumented wallet/PXE/node/RPC/WASM span as a horizontal bar + * on a time axis. Nesting is derived from timing containment (parent spans + * fully enclose child spans). Click row labels to collapse/expand subtrees. + */ + +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 CATEGORY_COLORS: Record = { + wallet: '#5c7cfa', + pxe: '#ce93d8', + sim: '#ffd54f', + oracle: '#ffab40', + node: '#66bb6a', + rpc: '#4fc3f7', + wasm: '#ff7043', +}; + +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 containment tree. A record A "contains" B when: + * A.start <= B.start AND A.start + A.duration >= B.start + B.duration + * We pick the tightest (smallest-duration) container as parent. + */ +function buildTree(records: ProfileRecord[]): TreeNode[] { + if (records.length === 0) return []; + + // Sort: earlier start first, longer duration first (parents before children) + const sorted = [...records].sort( + (a, b) => a.start - b.start || b.duration - a.duration, + ); + + // Stable IDs from record content (disambiguated by occurrence count) + const idCounts = new Map(); + function makeId(r: ProfileRecord): string { + const base = `${r.start.toFixed(3)}|${r.category}|${r.name}`; + const n = idCounts.get(base) ?? 0; + idCounts.set(base, n + 1); + return n === 0 ? base : `${base}#${n}`; + } + + const nodes: TreeNode[] = sorted.map(r => ({ + id: makeId(r), + record: r, + children: [], + depth: 0, + })); + const roots: TreeNode[] = []; + + for (let i = 0; i < nodes.length; i++) { + const c = nodes[i].record; + const cEnd = c.start + c.duration; + let bestParent: TreeNode | null = null; + + for (let j = 0; j < i; j++) { + const p = nodes[j].record; + const pEnd = p.start + p.duration; + if (p.start <= c.start && pEnd >= cEnd) { + if (!bestParent || p.duration < bestParent.record.duration) { + bestParent = nodes[j]; + } + } + } + + if (bestParent) { + bestParent.children.push(nodes[i]); + nodes[i].depth = bestParent.depth + 1; + } else { + roots.push(nodes[i]); + } + } + + return roots; +} + +// ─── Layout ────────────────────────────────────────────────────────────────── + +interface LayoutItem { + record: ProfileRecord; + nodeId: string; + depth: number; + row: number; + hasChildren: boolean; +} + +/** + * Flatten the tree into row assignments, respecting collapsed state. + * + * Each node occupies one row. Its children are laid out directly below: + * 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. + * + * This keeps parallel calls (batched RPCs) visually adjacent and maintains + * chronological order between sequential circuit executions and their + * surrounding oracle calls. + */ +function layoutTree( + roots: TreeNode[], + expanded: Set, +): { items: LayoutItem[]; expandableIds: Set } { + const items: LayoutItem[] = []; + const expandableIds = new Set(); + let nextRow = 0; + + // Only true leaves get lane-packed. Nodes with children always get their + // own row so each has its own label + collapse triangle. + 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. + // Children whose time ranges overlap form a group. This keeps parallel + // calls (e.g. batched RPCs) together and preserves chronological order + // between sequential blocks and their surrounding oracle calls. + 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) { + // All leaves → lane-pack 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 or blocks → 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 ViewRange { + start: number; + end: number; +} + +interface HoverInfo { + item: LayoutItem; + x: number; + y: number; +} + +function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDuration: number }) { + const canvasRef = useRef(null); + const containerRef = useRef(null); + const scrollRef = useRef(null); + const [hover, setHover] = useState(null); + const [viewportW, setViewportW] = useState(800); + // Zoom: 1 = fit everything, 2 = 2x, etc. + const [zoom, setZoom] = useState(1); + // Everything starts collapsed; click a bar to expand it. + const [expanded, setExpanded] = useState>(new Set()); + + // Filter records by min duration + const filtered = useMemo( + () => report.records.filter(r => r.duration >= minDuration), + [report, minDuration], + ); + + // Build stable tree (doesn't depend on expand state) + const tree = useMemo(() => buildTree(filtered), [filtered]); + + // Helper maps for sibling-aware expand/collapse + const { parentMap, childrenMap } = useMemo(() => { + const pMap = new Map(); + const cMap = new Map(); + function walk(node: TreeNode, parentId: string | null) { + pMap.set(node.id, parentId); + cMap.set(node.id, node.children.map(c => c.id)); + for (const child of node.children) walk(child, node.id); + } + for (const root of tree) walk(root, null); + return { parentMap: pMap, childrenMap: cMap }; + }, [tree]); + + // Root IDs for sibling lookup of top-level nodes + const rootIds = useMemo(() => tree.map(r => r.id), [tree]); + + // Toggle expand: expanding a node collapses its siblings (accordion style) + const toggleExpand = useCallback((nodeId: string) => { + setExpanded(prev => { + const next = new Set(prev); + if (next.has(nodeId)) { + // Collapse: remove this node + 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 { + // Expand: collapse siblings first (accordion), then expand this + const parentId = parentMap.get(nodeId); + const siblings = parentId != null + ? (childrenMap.get(parentId) ?? []) + : rootIds; + const removeDesc = (id: string) => { + for (const child of childrenMap.get(id) ?? []) { + next.delete(child); + removeDesc(child); + } + }; + for (const sib of siblings) { + if (sib !== nodeId) { + next.delete(sib); + removeDesc(sib); + } + } + next.add(nodeId); + } + return next; + }); + }, [parentMap, childrenMap, rootIds]); + + // Layout (depends on expand state) + const { items: layout, expandableIds } = useMemo( + () => layoutTree(tree, expanded), + [tree, expanded], + ); + const layoutRef = useRef(layout); + layoutRef.current = layout; + + const numRows = layout.length > 0 ? Math.max(...layout.map(l => l.row)) + 1 : 1; + const canvasH = numRows * ROW_STEP + 4; + const totalMs = report.durationMs; + + // Resize observer + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const ro = new ResizeObserver(entries => { + setCanvasW(entries[0].contentRect.width); + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + // ── Draw ─────────────────────────────────────────────────────────────────── + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const dpr = window.devicePixelRatio || 1; + canvas.width = canvasW * dpr; + canvas.height = canvasH * dpr; + ctx.scale(dpr, dpr); + + const { start: vs, end: ve } = view; + const span = ve - vs || 1; + const chartW = canvasW - LABEL_W; + const msToX = (ms: number) => LABEL_W + ((ms - vs) / span) * chartW; + + ctx.clearRect(0, 0, canvasW, canvasH); + ctx.font = FONT; + + // Alternating row backgrounds + for (let r = 0; r < numRows; r++) { + const y = r * ROW_STEP; + ctx.fillStyle = r % 2 === 0 ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0)'; + ctx.fillRect(0, y, canvasW, ROW_STEP); + } + + // Separator line + ctx.strokeStyle = 'rgba(255,255,255,0.08)'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(LABEL_W, 0); + ctx.lineTo(LABEL_W, canvasH); + ctx.stroke(); + + // Grid lines + const rawStep = span / 6; + const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); + const step = [1, 2, 5, 10].map(n => n * mag).find(s => span / s <= 8) ?? mag; + const firstTick = Math.ceil(vs / step) * step; + ctx.strokeStyle = 'rgba(255,255,255,0.05)'; + for (let t = firstTick; t <= ve; t += step) { + const x = Math.round(msToX(t)) + 0.5; + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, canvasH); + ctx.stroke(); + } + + // Bars + labels + const drawnLabelRows = new Set(); + for (const item of layout) { + const r = item.record; + const rEnd = r.start + r.duration; + if (rEnd < vs || r.start > ve) continue; + + const x = msToX(Math.max(r.start, vs)); + const w = Math.max(MIN_BAR_PX, msToX(Math.min(rEnd, ve)) - x); + const y = item.row * ROW_STEP + 1; + const color = CATEGORY_COLORS[r.category]; + + // Bar + 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; + + // Bar label (inside bar if wide enough) + if (w > 50) { + ctx.fillStyle = 'rgba(0,0,0,0.85)'; + ctx.save(); + ctx.beginPath(); + ctx.rect(x + 2, y, w - 4, ROW_H); + ctx.clip(); + ctx.fillText(`${r.name} ${fmt(r.duration)}`, x + 3, y + ROW_H - 6); + ctx.restore(); + } + + // Row label (left column, first item per row wins) + if (!drawnLabelRows.has(item.row)) { + drawnLabelRows.add(item.row); + const indent = item.depth * INDENT_PX; + const labelX = 4 + indent; + const cy = y + ROW_H / 2; + + // Collapse triangle for expandable nodes + if (item.hasChildren) { + const isExp = expanded.has(item.nodeId); + ctx.fillStyle = 'rgba(255,255,255,0.55)'; + 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(); + } + + // Label text + const textX = labelX + (item.hasChildren ? 11 : 0); + ctx.fillStyle = 'rgba(255,255,255,0.5)'; + ctx.save(); + ctx.beginPath(); + ctx.rect(textX, y + 2, LABEL_W - textX - 4, ROW_H - 2); + ctx.clip(); + ctx.fillText(r.name, textX, y + ROW_H - 6); + ctx.restore(); + } + } + }, [view, canvasW, canvasH, layout, numRows, expanded]); + + // ── Hit test (chart area) ────────────────────────────────────────────────── + const hitTest = useCallback((clientX: number, clientY: number): LayoutItem | null => { + const canvas = canvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + const x = clientX - rect.left; + const y = clientY - rect.top; + if (x < LABEL_W) return null; + const { start: vs, end: ve } = viewRef.current; + const span = ve - vs || 1; + const chartW = rect.width - LABEL_W; + const ms = vs + ((x - LABEL_W) / chartW) * span; + const row = Math.floor(y / 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; + }, []); + + // ── Hit test (label area — for collapse toggle) ──────────────────────────── + const hitLabel = useCallback((clientX: number, clientY: number): LayoutItem | null => { + const canvas = canvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + const x = clientX - rect.left; + if (x >= LABEL_W) return null; + const y = clientY - rect.top; + const row = Math.floor(y / ROW_STEP); + for (const item of layoutRef.current) { + if (item.row === row && item.hasChildren) return item; + } + return null; + }, []); + + // ── Mouse events ─────────────────────────────────────────────────────────── + const onMouseDown = useCallback((e: React.MouseEvent) => { + if (e.button !== 0) return; + const rect = canvasRef.current!.getBoundingClientRect(); + const x = e.clientX - rect.left; + // Don't start drag in label area (that's for collapse toggle) + if (x < LABEL_W) return; + dragRef.current = { x0: x, moved: false }; + setDragX({ x0: x, x1: x }); + setHover(null); + }, []); + + const onMouseMove = useCallback((e: React.MouseEvent) => { + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + + if (dragRef.current) { + const x1 = x; + if (Math.abs(x1 - dragRef.current.x0) > 3) dragRef.current.moved = true; + setDragX({ x0: dragRef.current.x0, x1 }); + return; + } + + if (x < LABEL_W) { + // Label area — show pointer if expandable node + const item = hitLabel(e.clientX, e.clientY); + canvas.style.cursor = item ? 'pointer' : 'default'; + setHover(null); + return; + } + + const item = hitTest(e.clientX, e.clientY); + if (item) { + setHover({ item, x: e.clientX, y: e.clientY }); + canvas.style.cursor = 'pointer'; + } else { + setHover(null); + canvas.style.cursor = 'crosshair'; + } + }, [hitTest, hitLabel]); + + const onMouseUp = useCallback((e: React.MouseEvent) => { + const rect = canvasRef.current!.getBoundingClientRect(); + const x = e.clientX - rect.left; + + // Label area click → toggle expand/collapse + if (x < LABEL_W && !dragRef.current) { + const item = hitLabel(e.clientX, e.clientY); + if (item) toggleExpand(item.nodeId); + return; + } + + if (!dragRef.current) return; + const { x0, moved } = dragRef.current; + dragRef.current = null; + setDragX(null); + + // Single click on a bar → expand/collapse it + if (!moved || Math.abs(x - x0) < 4) { + const item = hitTest(e.clientX, e.clientY); + if (item?.hasChildren) toggleExpand(item.nodeId); + return; + } + + // Drag → zoom + const { start: vs, end: ve } = viewRef.current; + const span = ve - vs; + const chartW = rect.width - LABEL_W; + const lo = Math.max(0, Math.min(x0, x) - LABEL_W); + const hi = Math.max(0, Math.max(x0, x) - LABEL_W); + const newStart = vs + (lo / chartW) * span; + const newEnd = vs + (hi / chartW) * span; + if (newEnd > newStart) setView({ start: newStart, end: newEnd }); + }, [hitLabel, hitTest, toggleExpand]); + + const onDblClick = useCallback(() => { + setView({ start: 0, end: totalMs }); + }, [totalMs]); + + const onMouseLeave = useCallback(() => { + if (dragRef.current) { + dragRef.current = null; + setDragX(null); + } + setHover(null); + }, []); + + const isZoomed = view.end - view.start < totalMs * 0.99; + + return ( + + {/* Toolbar */} + + + {isZoomed + ? `${fmt(view.start)} \u2013 ${fmt(view.end)} (${fmt(view.end - view.start)}) \u00b7 drag to zoom \u00b7 dbl-click reset` + : `${fmt(totalMs)} total \u00b7 ${filtered.length} spans \u00b7 drag to zoom \u00b7 click labels to collapse`} + + {isZoomed && ( + + )} + {expanded.size > 0 && ( + + )} + {expanded.size < expandableIds.size && expandableIds.size > 0 && ( + + )} + + + {/* Canvas */} + + + {dragX && (() => { + const left = Math.min(dragX.x0, dragX.x1); + const width = Math.abs(dragX.x1 - dragX.x0); + return ( + + ); + })()} + + + {/* Time axis */} + + {(() => { + const { start: vs, end: ve } = view; + const span = ve - vs || 1; + const rawStep = span / 6; + const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); + const step = [1, 2, 5, 10].map(n => n * mag).find(s => span / s <= 8) ?? mag; + const first = Math.ceil(vs / step) * step; + const ticks: number[] = []; + for (let t = first; t <= ve; t += step) ticks.push(t); + return ticks.map(t => ( + + + {fmt(t)} + + + )); + })()} + + + {/* 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 ─────────────────────────────────────────────────────────── + +function SummaryTable({ report, minDuration }: { report: ProfileReport; minDuration: number }) { + const filtered = report.records.filter(r => r.duration >= minDuration); + + // Aggregate by category + 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); + } + + // Circuit executions (sim category) — all of them, not just top N + const simAgg = new Map(); + for (const r of report.records.filter(r => r.category === 'sim')) { + const entry = simAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; + entry.count++; + entry.totalMs += r.duration; + if (r.duration > entry.maxMs) entry.maxMs = r.duration; + simAgg.set(r.name, entry); + } + const allSim = [...simAgg.entries()] + .sort((a, b) => b[1].totalMs - a[1].totalMs); + + // Top WASM operations by total time + const wasmAgg = new Map(); + for (const r of report.records.filter(r => r.category === 'wasm')) { + const entry = wasmAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; + entry.count++; + entry.totalMs += r.duration; + if (r.duration > entry.maxMs) entry.maxMs = r.duration; + wasmAgg.set(r.name, entry); + } + const topWasm = [...wasmAgg.entries()] + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, 15); + + // Top RPC calls + const rpcAgg = new Map(); + for (const r of report.records.filter(r => r.category === 'rpc')) { + const entry = rpcAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; + entry.count++; + entry.totalMs += r.duration; + if (r.duration > entry.maxMs) entry.maxMs = r.duration; + rpcAgg.set(r.name, entry); + } + const topRpc = [...rpcAgg.entries()] + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, 15); + + const tableStyle = { fontSize: 10, color: 'rgba(255,255,255,0.6)', fontFamily: 'monospace', py: 0.25, px: 1 }; + + return ( + + {/* Category breakdown */} + + + 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)} + + ))} + + + + + {/* Circuit executions */} + {allSim.length > 0 && ( + + + CIRCUIT EXECUTIONS + + + + {allSim.map(([name, { count, totalMs, maxMs }]) => ( + + {name} + ×{count} + {fmt(totalMs)} + max {fmt(maxMs)} + + ))} + + + + )} + + {/* Top WASM ops */} + {topWasm.length > 0 && ( + + + TOP WASM + + + + {topWasm.map(([name, { count, totalMs, maxMs }]) => ( + + {name} + ×{count} + {fmt(totalMs)} + max {fmt(maxMs)} + + ))} + + + + )} + + {/* Top RPC calls */} + {topRpc.length > 0 && ( + + + TOP RPC + + + + {topRpc.map(([name, { count, totalMs, maxMs }]) => ( + + {name} + ×{count} + {fmt(totalMs)} + max {fmt(maxMs)} + + ))} + + + + )} + + ); +} + +// ─── Full-screen profile page (portal) ────────────────────────────────────── + +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 \u2014 {report.name} \u2014 {fmt(report.durationMs)} + + + + + {/* Min duration filter */} + + 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 [enabled] = useState(() => new URLSearchParams(location.search).has('profile')); + const [recording, setRecording] = useState(false); + const [report, setReport] = useState(null); + const [fullscreen, setFullscreen] = useState(false); + const instrumentedRef = useRef(false); + const { wallet } = useWallet(); + + // Install global interceptors on mount + useEffect(() => { + if (!enabled) return; + profiler.install(); + }, [enabled]); + + // Instrument wallet when available + useEffect(() => { + if (!enabled || !wallet || instrumentedRef.current) return; + profiler.instrumentWallet(wallet); + instrumentedRef.current = true; + console.info('[profiler] Wallet instrumented'); + }, [enabled, 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]); + + if (!enabled) return null; + + 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/profiling/index.ts b/src/profiling/index.ts new file mode 100644 index 0000000..26f70df --- /dev/null +++ b/src/profiling/index.ts @@ -0,0 +1,255 @@ +/** + * Profiling orchestrator. + * + * Instruments the embedded wallet, PXE, node client, fetch, and WASM from + * the outside — no wallet code changes needed. + * + * Usage: + * await profiler.install(); // global interceptors (fetch, WASM) + * profiler.instrumentWallet(wallet); // wrap wallet + its PXE + node + * profiler.start('sendTx'); + * // ... perform operation ... + * const report = profiler.stop(); + */ + +import { installFetchInterceptor, installWasmInterceptor, installSimulatorInterceptorFromPXE } from './interceptors'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +export type Category = 'wallet' | 'pxe' | 'sim' | 'oracle' | 'node' | 'rpc' | 'wasm'; + +export interface ProfileRecord { + name: string; + category: Category; + start: number; // ms from recording origin + duration: number; // ms + detail?: string; + error?: boolean; +} + +export interface ProfileReport { + name: string; + startedAt: number; // Date.now() at recording start + durationMs: number; + records: ProfileRecord[]; +} + +// ─── Method wrapping ───────────────────────────────────────────────────────── + +// Methods to skip — internal noise, getters, or things that break if wrapped. +const SKIP = new Set([ + 'constructor', 'toString', 'toJSON', 'valueOf', + 'then', // wrapping 'then' would break Promise detection + 'log', // logger getter +]); + +/** + * 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); + + 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); + const t0 = performance.now(); + let result: any; + try { + result = original.apply(this, args); + } catch (e) { + profiler.record(name, category, t0, performance.now() - t0, undefined, true); + throw e; + } + if (result && typeof result.then === 'function') { + return result.then( + (v: any) => { + profiler.record(name, category, t0, performance.now() - t0); + return v; + }, + (e: any) => { + profiler.record(name, category, t0, performance.now() - t0, undefined, true); + throw e; + }, + ); + } + profiler.record(name, category, t0, performance.now() - t0); + return result; + }; + (wrapped as any).__profiled = true; + target[name] = wrapped; + restores.push(() => { target[name] = original; }); + } + + return () => restores.forEach(r => r()); +} + +// ─── Profiler ──────────────────────────────────────────────────────────────── + +class Profiler { + private _recording = false; + private _origin = 0; + private _startedAt = 0; + private _name = ''; + private _records: ProfileRecord[] = []; + private _cleanups: (() => void)[] = []; + private _installed = false; + + get isRecording() { return this._recording; } + get isInstalled() { return this._installed; } + + /** Push a completed record. Called by interceptors and method wrappers. */ + record( + name: string, + category: Category, + startAbsolute: number, + duration: number, + detail?: string, + error?: boolean, + ) { + if (!this._recording) return; + this._records.push({ + name, + category, + start: startAbsolute - this._origin, + duration, + detail, + error, + }); + } + + /** Install global interceptors (fetch, bb.js WASM). Call once, before wallet creation ideally. */ + async install() { + if (this._installed) return; + const recFn = this.record.bind(this); + const isRec = () => this._recording; + this._cleanups.push(installFetchInterceptor(recFn, isRec)); + this._cleanups.push(await installWasmInterceptor(recFn, isRec)); + this._installed = true; + } + + /** Wrap a wallet instance + its internal PXE + node. Call once per wallet. */ + instrumentWallet(wallet: any) { + this._cleanups.push(wrapAllMethods(wallet, 'wallet', this)); + + const pxe = wallet.pxe; + if (pxe) { + this._cleanups.push(wrapAllMethods(pxe, 'pxe', this)); + // Patch circuit simulator prototypes (ACVM witness generation) + const recFn = this.record.bind(this); + const isRec = () => this._recording; + this._cleanups.push(installSimulatorInterceptorFromPXE(pxe, recFn, isRec)); + } + + const node = wallet.aztecNode; + if (node) { + this._cleanups.push(wrapAllMethods(node, 'node', this)); + } + } + + start(name = 'profile') { + if (this._recording) return; + this._name = name; + this._origin = performance.now(); + this._startedAt = Date.now(); + this._records = []; + this._recording = true; + console.info(`[profiler] Started: "${name}"`); + } + + stop(): ProfileReport { + if (!this._recording) { + return { name: '', startedAt: 0, durationMs: 0, records: [] }; + } + this._recording = false; + const durationMs = performance.now() - this._origin; + const records = this.deduplicateBatchedCalls([...this._records]); + const report: ProfileReport = { + name: this._name, + startedAt: this._startedAt, + durationMs, + records, + }; + console.info( + `[profiler] Stopped: "${this._name}" — ${(durationMs / 1000).toFixed(2)}s, ` + + `${report.records.length} spans`, + ); + return report; + } + + /** + * When the node client batches multiple calls into one fetch, we capture + * both the individual node method spans AND the batch rpc span — same + * timing, redundant visual noise. Remove the individual node/rpc records + * that are covered by a batch. + */ + private deduplicateBatchedCalls(records: ProfileRecord[]): ProfileRecord[] { + const batches = records.filter( + r => r.category === 'rpc' && r.name.startsWith('[batch]'), + ); + if (batches.length === 0) return records; + + return records.filter(r => { + // Only deduplicate node-level and individual rpc records + if (r.category !== 'node' && r.category !== 'rpc') return true; + // Keep batch records themselves + if (r.name.startsWith('[batch]')) return true; + + const rEnd = r.start + r.duration; + + for (const batch of batches) { + const batchEnd = batch.start + batch.duration; + // Check timing overlap (the node call and batch should overlap) + if (batch.start > rEnd || batchEnd < r.start) continue; + // Check if the batch label mentions this method name + if (batch.name.includes(r.name)) return false; + } + return true; + }); + } + + 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(); diff --git a/src/profiling/interceptors.ts b/src/profiling/interceptors.ts new file mode 100644 index 0000000..ec72c4c --- /dev/null +++ b/src/profiling/interceptors.ts @@ -0,0 +1,393 @@ +/** + * Fetch + WASM interception for profiling. + * + * Fetch: patches window.fetch to extract JSON-RPC method names and record timing. + * WASM: patches BarretenbergSync/Barretenberg backend.call to decode msgpack + * operation names and record timing for every bb.js API call. + */ + +// ─── Callback interface ───────────────────────────────────────────────────── + +export type RecordFn = ( + name: string, + category: 'rpc' | 'wasm' | 'sim' | 'oracle', + startAbsolute: number, + duration: number, + detail?: string, + error?: boolean, +) => void; + +export type IsRecording = () => boolean; + +// ─── Fetch interceptor ────────────────────────────────────────────────────── + +export function installFetchInterceptor( + record: RecordFn, + isRecording: IsRecording, +): () => void { + const original = window.fetch.bind(window); + + window.fetch = async ( + input: RequestInfo | URL, + init?: RequestInit, + ): Promise => { + const recording = isRecording(); + if (!recording) return original(input, init); + + const url = + typeof input === 'string' + ? input + : input instanceof URL + ? input.href + : input.url; + const t0 = performance.now(); + + // Extract JSON-RPC method name(s) from request body + let method = ''; + let batched = false; + if (init?.body && typeof init.body === 'string') { + 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 { + /* not JSON */ + } + } + if (!method) { + try { + method = new URL(url, location.href).pathname; + } catch { + method = url; + } + } + + const label = batched ? `[batch] ${method}` : method; + + try { + const response = await original(input, init); + record(label, 'rpc', t0, performance.now() - t0, `${response.status}`); + return response; + } catch (e) { + record(label, 'rpc', t0, performance.now() - t0, 'network error', true); + throw e; + } + }; + + 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, + record: RecordFn, + isRecording: IsRecording, + 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 (!isRecording()) return original(inputBuffer); + const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_sync'; + const t0 = performance.now(); + const result = original(inputBuffer); + record(opName, 'wasm', t0, performance.now() - t0); + return result; + }; + } else { + backend.call = async function (inputBuffer: Uint8Array) { + if (!isRecording()) return original(inputBuffer); + const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_async'; + const t0 = performance.now(); + try { + const result = await original(inputBuffer); + record(opName, 'wasm', t0, performance.now() - t0); + return result; + } catch (err) { + record(opName, 'wasm', t0, performance.now() - t0, undefined, true); + throw err; + } + }; + } + + backend.call.__profiled = true; + const restore = () => { + backend.call = original; + }; + return restore; +} + +export async function installWasmInterceptor( + record: RecordFn, + isRecording: IsRecording, +): 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) { + // Wrap existing singleton if already initialized + try { + const existing = BBSync.getSingleton(); + if (existing?.backend) + restores.push( + wrapBackendCall(existing.backend, record, isRecording, true), + ); + } catch { + /* not yet init'd */ + } + + // Wrap future singletons + 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, record, isRecording, 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, record, isRecording, 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, record, isRecording, 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 interceptor ────────────────────────────────────────────────── +// Patches prototype methods on the circuit simulator (ACVM witness generation) +// reached through the PXE instance — no problematic imports needed. + +function wrapProtoMethod( + proto: any, + method: string, + record: RecordFn, + isRecording: IsRecording, + label: (args: any[]) => string, +): () => void { + const original = proto[method]; + if (typeof original !== 'function' || original.__profiled) return () => {}; + + proto[method] = function (this: any, ...args: any[]) { + if (!isRecording()) return original.apply(this, args); + const name = label(args); + const t0 = performance.now(); + let result: any; + try { + result = original.apply(this, args); + } catch (e) { + record(name, 'sim', t0, performance.now() - t0, undefined, true); + throw e; + } + if (result && typeof result.then === 'function') { + return result.then( + (v: any) => { record(name, 'sim', t0, performance.now() - t0); return v; }, + (e: any) => { record(name, 'sim', t0, performance.now() - t0, undefined, true); throw e; }, + ); + } + record(name, 'sim', t0, performance.now() - t0); + return result; + }; + proto[method].__profiled = true; + return () => { proto[method] = original; }; +} + +/** + * Wrap every method on an ACIRCallback (oracle) object with profiling. + * The callback is Record Promise<...>>. + * Each key is an oracle function name (getNotes, getPublicDataTreeWitness, etc.). + */ +function wrapOracleCallback( + callback: any, + record: RecordFn, + isRecording: IsRecording, +): 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] = async function (...args: any[]) { + if (!isRecording()) return original.apply(this, args); + const t0 = performance.now(); + try { + const result = await original.apply(this, args); + record(key, 'oracle', t0, performance.now() - t0); + return result; + } catch (e) { + record(key, 'oracle', t0, performance.now() - t0, undefined, true); + throw e; + } + }; + } + 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 (getNotes, getPublicDataTreeWitness, etc.) + * gets its own span. + * - executeProtocolCircuit: records protocol circuit execution. + */ +export function installSimulatorInterceptorFromPXE( + pxe: any, + record: RecordFn, + isRecording: IsRecording, +): () => void { + const restores: (() => void)[] = []; + + const sim = pxe?.simulator; + if (!sim) return () => {}; + + const simProto = Object.getPrototypeOf(sim); + if (!simProto) return () => {}; + + // executeUserCircuit(input, artifact, callback) + // We wrap the method AND the oracle callback (3rd arg). + if (typeof simProto.executeUserCircuit === 'function' && !simProto.executeUserCircuit.__profiled) { + const original = simProto.executeUserCircuit; + simProto.executeUserCircuit = async function (this: any, input: any, artifact: any, callback: any, ...rest: any[]) { + if (!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, record, isRecording); + + const t0 = performance.now(); + try { + const result = await original.call(this, input, artifact, wrappedCallback, ...rest); + record(label, 'sim', t0, performance.now() - t0); + return result; + } catch (e) { + record(label, 'sim', t0, performance.now() - t0, undefined, true); + throw e; + } + }; + simProto.executeUserCircuit.__profiled = true; + restores.push(() => { simProto.executeUserCircuit = original; }); + } + + // executeProtocolCircuit(input, artifact, callback) + if (typeof simProto.executeProtocolCircuit === 'function' && !simProto.executeProtocolCircuit.__profiled) { + const original = simProto.executeProtocolCircuit; + simProto.executeProtocolCircuit = async function (this: any, input: any, artifact: any, callback: any, ...rest: any[]) { + if (!isRecording()) return original.call(this, input, artifact, callback, ...rest); + + const label = artifact?.name ?? 'protocol_circuit'; + // Protocol circuits also get oracle callback wrapping (for ForeignCallHandler) + const wrappedCallback = callback && typeof callback === 'object' + ? wrapOracleCallback(callback, record, isRecording) + : callback; + + const t0 = performance.now(); + try { + const result = await original.call(this, input, artifact, wrappedCallback, ...rest); + record(label, 'sim', t0, performance.now() - t0); + return result; + } catch (e) { + record(label, 'sim', t0, performance.now() - t0, undefined, true); + throw e; + } + }; + simProto.executeProtocolCircuit.__profiled = true; + restores.push(() => { simProto.executeProtocolCircuit = original; }); + } + + return () => restores.forEach((r) => r()); +} diff --git a/vite.config.ts b/vite.config.ts index 2951983..6cdf4c4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -103,6 +103,8 @@ export default defineConfig(({ mode }) => { headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp', + // Required for the JS Self-Profiling API (in-page sampling profiler) + 'Document-Policy': 'js-profiling', }, fs: { allow: [searchForWorkspaceRoot(process.cwd())], From 9a9baea7bad0616631163b38c3e3cc92cf860b68 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 15 Apr 2026 15:40:28 +0200 Subject: [PATCH 2/6] working --- package.json | 3 +- src/App.tsx | 14 +- src/app-entry.tsx | 24 + src/components/ProfilePanel.tsx | 964 ++++++++++++++++---------------- src/main.tsx | 52 +- src/profiling/context.ts | 95 ++++ src/profiling/index.ts | 270 +++++---- src/profiling/interceptors.ts | 274 +++------ src/profiling/types.ts | 34 ++ vite.config.ts | 23 +- yarn.lock | 8 + 11 files changed, 971 insertions(+), 790 deletions(-) create mode 100644 src/app-entry.tsx create mode 100644 src/profiling/context.ts create mode 100644 src/profiling/types.ts diff --git a/package.json b/package.json index 3159188..6fdc890 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "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", diff --git a/src/App.tsx b/src/App.tsx index d072ae2..45d8b4e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,9 +12,12 @@ import { TxNotificationCenter } from '@gregojuice/embedded-wallet/ui'; import type { AztecAddress } from '@aztec/aztec.js/addresses'; import { lazy, Suspense } from 'react'; -const ProfilePanel = lazy(() => - import('./components/ProfilePanel').then(m => ({ default: m.ProfilePanel })), -); +// 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(); @@ -143,8 +146,9 @@ export function App() { {/* Transaction Progress Toasts (embedded wallet only) */} - {/* Performance profiling panel — activate with ?profile=1 */} - {new URLSearchParams(location.search).has('profile') && ( + {/* 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 index f2f9ecc..cba5e28 100644 --- a/src/components/ProfilePanel.tsx +++ b/src/components/ProfilePanel.tsx @@ -1,10 +1,19 @@ /** * Profiling panel — canvas-based waterfall chart. - * Activated by ?profile query parameter. * - * Shows every instrumented wallet/PXE/node/RPC/WASM span as a horizontal bar - * on a time axis. Nesting is derived from timing containment (parent spans - * fully enclose child spans). Click row labels to collapse/expand subtrees. + * 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'; @@ -22,17 +31,20 @@ 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`; @@ -46,58 +58,41 @@ interface TreeNode { } /** - * Build a containment tree. A record A "contains" B when: - * A.start <= B.start AND A.start + A.duration >= B.start + B.duration - * We pick the tightest (smallest-duration) container as parent. + * 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 []; - // Sort: earlier start first, longer duration first (parents before children) - const sorted = [...records].sort( - (a, b) => a.start - b.start || b.duration - a.duration, - ); - - // Stable IDs from record content (disambiguated by occurrence count) - const idCounts = new Map(); - function makeId(r: ProfileRecord): string { - const base = `${r.start.toFixed(3)}|${r.category}|${r.name}`; - const n = idCounts.get(base) ?? 0; - idCounts.set(base, n + 1); - return n === 0 ? base : `${base}#${n}`; + const byId = new Map(); + for (const r of records) { + byId.set(r.id, { id: r.id, record: r, children: [], depth: 0 }); } - const nodes: TreeNode[] = sorted.map(r => ({ - id: makeId(r), - record: r, - children: [], - depth: 0, - })); const roots: TreeNode[] = []; - - for (let i = 0; i < nodes.length; i++) { - const c = nodes[i].record; - const cEnd = c.start + c.duration; - let bestParent: TreeNode | null = null; - - for (let j = 0; j < i; j++) { - const p = nodes[j].record; - const pEnd = p.start + p.duration; - if (p.start <= c.start && pEnd >= cEnd) { - if (!bestParent || p.duration < bestParent.record.duration) { - bestParent = nodes[j]; - } - } - } - - if (bestParent) { - bestParent.children.push(nodes[i]); - nodes[i].depth = bestParent.depth + 1; + 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(nodes[i]); + 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; } @@ -112,17 +107,12 @@ interface LayoutItem { } /** - * Flatten the tree into row assignments, respecting collapsed state. + * Flatten the tree into row assignments, respecting the expanded set. * - * Each node occupies one row. Its children are laid out directly below: - * 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. - * - * This keeps parallel calls (batched RPCs) visually adjacent and maintains - * chronological order between sequential circuit executions and their - * surrounding oracle calls. + * 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[], @@ -132,8 +122,6 @@ function layoutTree( const expandableIds = new Set(); let nextRow = 0; - // Only true leaves get lane-packed. Nodes with children always get their - // own row so each has its own label + collapse triangle. function isLeafLike(node: TreeNode): boolean { return node.children.length === 0; } @@ -156,10 +144,7 @@ function layoutTree( (a, b) => a.record.start - b.record.start, ); - // Group children into temporal clusters. - // Children whose time ranges overlap form a group. This keeps parallel - // calls (e.g. batched RPCs) together and preserves chronological order - // between sequential blocks and their surrounding oracle calls. + // Group children into temporal clusters so parallel calls stay adjacent. type Group = { nodes: TreeNode[]; end: number }; const groups: Group[] = []; for (const child of children) { @@ -177,36 +162,27 @@ function layoutTree( const allLeafLike = group.nodes.every(c => isLeafLike(c)); if (allLeafLike) { - // All leaves → lane-pack for compactness + // 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); - } + 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, + record: child.record, nodeId: child.id, + depth: child.depth, row: baseRow + lane, hasChildren: false, }); } nextRow += Math.max(1, laneEnds.length); } else { - // Mixed group or blocks → each child in time order + // 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, + record: child.record, nodeId: child.id, + depth: child.depth, row: nextRow, hasChildren: false, }); nextRow++; } else { @@ -218,68 +194,70 @@ function layoutTree( } const sorted = [...roots].sort((a, b) => a.record.start - b.record.start); - for (const root of sorted) { - visitNode(root); - } - + for (const root of sorted) visitNode(root); return { items, expandableIds }; } // ─── Waterfall chart ───────────────────────────────────────────────────────── -interface ViewRange { - start: number; - end: number; -} - 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 canvasRef = useRef(null); - const containerRef = useRef(null); - const scrollRef = useRef(null); + const labelCanvasRef = useRef(null); + const chartCanvasRef = useRef(null); + const outerRef = useRef(null); const [hover, setHover] = useState(null); const [viewportW, setViewportW] = useState(800); - // Zoom: 1 = fit everything, 2 = 2x, etc. const [zoom, setZoom] = useState(1); - // Everything starts collapsed; click a bar to expand it. 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 ────────────────────────────────────────────────────────── - // Filter records by min duration const filtered = useMemo( () => report.records.filter(r => r.duration >= minDuration), [report, minDuration], ); - // Build stable tree (doesn't depend on expand state) const tree = useMemo(() => buildTree(filtered), [filtered]); - // Helper maps for sibling-aware expand/collapse - const { parentMap, childrenMap } = useMemo(() => { - const pMap = new Map(); + const childrenMap = useMemo(() => { const cMap = new Map(); - function walk(node: TreeNode, parentId: string | null) { - pMap.set(node.id, parentId); + function walk(node: TreeNode) { cMap.set(node.id, node.children.map(c => c.id)); - for (const child of node.children) walk(child, node.id); + for (const child of node.children) walk(child); } - for (const root of tree) walk(root, null); - return { parentMap: pMap, childrenMap: cMap }; + for (const root of tree) walk(root); + return cMap; }, [tree]); - // Root IDs for sibling lookup of top-level nodes - const rootIds = useMemo(() => tree.map(r => r.id), [tree]); - - // Toggle expand: expanding a node collapses its siblings (accordion style) const toggleExpand = useCallback((nodeId: string) => { setExpanded(prev => { const next = new Set(prev); if (next.has(nodeId)) { - // Collapse: remove this node + all its descendants + // Collapse this node and all its descendants. next.delete(nodeId); const removeDesc = (id: string) => { for (const child of childrenMap.get(id) ?? []) { @@ -289,30 +267,12 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur }; removeDesc(nodeId); } else { - // Expand: collapse siblings first (accordion), then expand this - const parentId = parentMap.get(nodeId); - const siblings = parentId != null - ? (childrenMap.get(parentId) ?? []) - : rootIds; - const removeDesc = (id: string) => { - for (const child of childrenMap.get(id) ?? []) { - next.delete(child); - removeDesc(child); - } - }; - for (const sib of siblings) { - if (sib !== nodeId) { - next.delete(sib); - removeDesc(sib); - } - } next.add(nodeId); } return next; }); - }, [parentMap, childrenMap, rootIds]); + }, [childrenMap]); - // Layout (depends on expand state) const { items: layout, expandableIds } = useMemo( () => layoutTree(tree, expanded), [tree, expanded], @@ -320,152 +280,238 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur 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 canvasH = numRows * ROW_STEP + 4; + 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 ──────────────────────────────────────────────────────── - // Resize observer useEffect(() => { - const el = containerRef.current; + const el = outerRef.current; if (!el) return; - const ro = new ResizeObserver(entries => { - setCanvasW(entries[0].contentRect.width); - }); + const ro = new ResizeObserver(entries => setViewportW(entries[0].contentRect.width)); ro.observe(el); return () => ro.disconnect(); }, []); - // ── Draw ─────────────────────────────────────────────────────────────────── + // 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 = canvasRef.current; + const canvas = labelCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const dpr = window.devicePixelRatio || 1; - canvas.width = canvasW * dpr; - canvas.height = canvasH * dpr; - ctx.scale(dpr, dpr); - - const { start: vs, end: ve } = view; - const span = ve - vs || 1; - const chartW = canvasW - LABEL_W; - const msToX = (ms: number) => LABEL_W + ((ms - vs) / span) * chartW; - - ctx.clearRect(0, 0, canvasW, canvasH); + 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; - // Alternating row backgrounds - for (let r = 0; r < numRows; r++) { - const y = r * ROW_STEP; + // 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, canvasW, ROW_STEP); + ctx.fillRect(0, y, LABEL_W, ROW_STEP); } - // Separator line + // Separator ctx.strokeStyle = 'rgba(255,255,255,0.08)'; ctx.lineWidth = 1; ctx.beginPath(); - ctx.moveTo(LABEL_W, 0); - ctx.lineTo(LABEL_W, canvasH); + ctx.moveTo(LABEL_W - 0.5, 0); + ctx.lineTo(LABEL_W - 0.5, CHART_HEIGHT); ctx.stroke(); - // Grid lines - const rawStep = span / 6; + // 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; + + // Collapse triangle + if (item.hasChildren) { + const isExp = expanded.has(item.nodeId); + ctx.fillStyle = 'rgba(255,255,255,0.55)'; + ctx.beginPath(); + if (isExp) { + ctx.moveTo(labelX, cy - 3); + ctx.lineTo(labelX + 8, cy - 3); + ctx.lineTo(labelX + 4, cy + 4); + } else { + 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 => span / s <= 8) ?? mag; - const firstTick = Math.ceil(vs / step) * step; + 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)'; - for (let t = firstTick; t <= ve; t += step) { + 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, canvasH); + ctx.lineTo(x, CHART_HEIGHT); ctx.stroke(); + ctx.fillStyle = 'rgba(255,255,255,0.25)'; + ctx.fillText(fmt(t), x + 3, 10); } - // Bars + labels - const drawnLabelRows = new Set(); + // 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 rEnd = r.start + r.duration; - if (rEnd < vs || r.start > ve) continue; - - const x = msToX(Math.max(r.start, vs)); - const w = Math.max(MIN_BAR_PX, msToX(Math.min(rEnd, ve)) - x); - const y = item.row * ROW_STEP + 1; + 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]; - // Bar 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; - // Bar label (inside bar if wide enough) if (w > 50) { ctx.fillStyle = 'rgba(0,0,0,0.85)'; ctx.save(); ctx.beginPath(); - ctx.rect(x + 2, y, w - 4, ROW_H); + ctx.rect(Math.max(0, x) + 2, y, Math.min(chartViewportW, x + w) - Math.max(0, x) - 4, ROW_H); ctx.clip(); - ctx.fillText(`${r.name} ${fmt(r.duration)}`, x + 3, y + ROW_H - 6); - ctx.restore(); - } - - // Row label (left column, first item per row wins) - if (!drawnLabelRows.has(item.row)) { - drawnLabelRows.add(item.row); - const indent = item.depth * INDENT_PX; - const labelX = 4 + indent; - const cy = y + ROW_H / 2; - - // Collapse triangle for expandable nodes - if (item.hasChildren) { - const isExp = expanded.has(item.nodeId); - ctx.fillStyle = 'rgba(255,255,255,0.55)'; - 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(); - } - - // Label text - const textX = labelX + (item.hasChildren ? 11 : 0); - ctx.fillStyle = 'rgba(255,255,255,0.5)'; - ctx.save(); - ctx.beginPath(); - ctx.rect(textX, y + 2, LABEL_W - textX - 4, ROW_H - 2); - ctx.clip(); - ctx.fillText(r.name, textX, y + ROW_H - 6); + ctx.fillText(`${r.name} ${fmt(r.duration)}`, Math.max(0, x) + 3, y + ROW_H - 6); ctx.restore(); } } - }, [view, canvasW, canvasH, layout, numRows, expanded]); + }, [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]); - // ── Hit test (chart area) ────────────────────────────────────────────────── - const hitTest = useCallback((clientX: number, clientY: number): LayoutItem | null => { - const canvas = canvasRef.current; + const chartHitTest = useCallback((e: React.MouseEvent): LayoutItem | null => { + const canvas = chartCanvasRef.current; if (!canvas) return null; const rect = canvas.getBoundingClientRect(); - const x = clientX - rect.left; - const y = clientY - rect.top; - if (x < LABEL_W) return null; - const { start: vs, end: ve } = viewRef.current; - const span = ve - vs || 1; - const chartW = rect.width - LABEL_W; - const ms = vs + ((x - LABEL_W) / chartW) * span; - const row = Math.floor(y / ROW_STEP); + 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; @@ -475,105 +521,120 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur } } return best; - }, []); + }, [chartViewportW, virtualContentW, totalMs, scrollLeft, scrollTop]); - // ── Hit test (label area — for collapse toggle) ──────────────────────────── - const hitLabel = useCallback((clientX: number, clientY: number): LayoutItem | null => { - const canvas = canvasRef.current; + const labelHitTest = useCallback((e: React.MouseEvent): LayoutItem | null => { + const canvas = labelCanvasRef.current; if (!canvas) return null; const rect = canvas.getBoundingClientRect(); - const x = clientX - rect.left; - if (x >= LABEL_W) return null; - const y = clientY - rect.top; - const row = Math.floor(y / ROW_STEP); + 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]); - // ── Mouse events ─────────────────────────────────────────────────────────── - const onMouseDown = useCallback((e: React.MouseEvent) => { + const onChartMouseDown = useCallback((e: React.MouseEvent) => { if (e.button !== 0) return; - const rect = canvasRef.current!.getBoundingClientRect(); - const x = e.clientX - rect.left; - // Don't start drag in label area (that's for collapse toggle) - if (x < LABEL_W) return; - dragRef.current = { x0: x, moved: false }; - setDragX({ x0: x, x1: x }); + 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 onMouseMove = useCallback((e: React.MouseEvent) => { - const canvas = canvasRef.current; + const onChartMouseMove = useCallback((e: React.MouseEvent) => { + const canvas = chartCanvasRef.current; if (!canvas) return; - const rect = canvas.getBoundingClientRect(); - const x = e.clientX - rect.left; if (dragRef.current) { - const x1 = x; - if (Math.abs(x1 - dragRef.current.x0) > 3) dragRef.current.moved = true; - setDragX({ x0: dragRef.current.x0, x1 }); - return; - } - - if (x < LABEL_W) { - // Label area — show pointer if expandable node - const item = hitLabel(e.clientX, e.clientY); - canvas.style.cursor = item ? 'pointer' : 'default'; - setHover(null); + 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 = hitTest(e.clientX, e.clientY); + const item = chartHitTest(e); + canvas.style.cursor = item ? 'pointer' : 'grab'; if (item) { setHover({ item, x: e.clientX, y: e.clientY }); - canvas.style.cursor = 'pointer'; } else { setHover(null); - canvas.style.cursor = 'crosshair'; - } - }, [hitTest, hitLabel]); - - const onMouseUp = useCallback((e: React.MouseEvent) => { - const rect = canvasRef.current!.getBoundingClientRect(); - const x = e.clientX - rect.left; - - // Label area click → toggle expand/collapse - if (x < LABEL_W && !dragRef.current) { - const item = hitLabel(e.clientX, e.clientY); - if (item) toggleExpand(item.nodeId); - return; } + }, [chartHitTest, chartViewportW, scrollLeft, maxScrollLeft, maxScrollTop]); + const onChartMouseUp = useCallback((e: React.MouseEvent) => { if (!dragRef.current) return; - const { x0, moved } = dragRef.current; + const { dragStartX, moved, shiftKey } = dragRef.current; + const endVirtualX = toVirtualX(e); dragRef.current = null; setDragX(null); - // Single click on a bar → expand/collapse it - if (!moved || Math.abs(x - x0) < 4) { - const item = hitTest(e.clientX, e.clientY); + if (!moved) { + // Single click → expand/collapse + const item = chartHitTest(e); if (item?.hasChildren) toggleExpand(item.nodeId); return; } - // Drag → zoom - const { start: vs, end: ve } = viewRef.current; - const span = ve - vs; - const chartW = rect.width - LABEL_W; - const lo = Math.max(0, Math.min(x0, x) - LABEL_W); - const hi = Math.max(0, Math.max(x0, x) - LABEL_W); - const newStart = vs + (lo / chartW) * span; - const newEnd = vs + (hi / chartW) * span; - if (newEnd > newStart) setView({ start: newStart, end: newEnd }); - }, [hitLabel, hitTest, toggleExpand]); - - const onDblClick = useCallback(() => { - setView({ start: 0, end: totalMs }); - }, [totalMs]); - - const onMouseLeave = useCallback(() => { + 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); @@ -581,90 +642,105 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur setHover(null); }, []); - const isZoomed = view.end - view.start < totalMs * 0.99; + // 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 */} - {isZoomed - ? `${fmt(view.start)} \u2013 ${fmt(view.end)} (${fmt(view.end - view.start)}) \u00b7 drag to zoom \u00b7 dbl-click reset` - : `${fmt(totalMs)} total \u00b7 ${filtered.length} spans \u00b7 drag to zoom \u00b7 click labels to collapse`} + {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 - {isZoomed && ( - + + {zoom > 1 && ( + )} {expanded.size > 0 && ( - )} {expanded.size < expandableIds.size && expandableIds.size > 0 && ( - )} - {/* Canvas */} - - - {dragX && (() => { - const left = Math.min(dragX.x0, dragX.x1); - const width = Math.abs(dragX.x1 - dragX.x0); - return ( - - ); - })()} - - - {/* Time axis */} - - {(() => { - const { start: vs, end: ve } = view; - const span = ve - vs || 1; - const rawStep = span / 6; - const mag = Math.pow(10, Math.floor(Math.log10(rawStep || 1))); - const step = [1, 2, 5, 10].map(n => n * mag).find(s => span / s <= 8) ?? mag; - const first = Math.ceil(vs / step) * step; - const ticks: number[] = []; - for (let t = first; t <= ve; t += step) ticks.push(t); - return ticks.map(t => ( - - - {fmt(t)} - - - )); - })()} + {/* 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 */} @@ -714,10 +790,23 @@ function SpanTooltip({ info, totalMs }: { info: HoverInfo; totalMs: number }) { // ─── 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); - // Aggregate by category const byCat = new Map(); for (const r of filtered) { const entry = byCat.get(r.category) ?? { count: 0, totalMs: 0 }; @@ -726,135 +815,83 @@ function SummaryTable({ report, minDuration }: { report: ProfileReport; minDurat byCat.set(r.category, entry); } - // Circuit executions (sim category) — all of them, not just top N - const simAgg = new Map(); - for (const r of report.records.filter(r => r.category === 'sim')) { - const entry = simAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; - entry.count++; - entry.totalMs += r.duration; - if (r.duration > entry.maxMs) entry.maxMs = r.duration; - simAgg.set(r.name, entry); - } - const allSim = [...simAgg.entries()] - .sort((a, b) => b[1].totalMs - a[1].totalMs); - - // Top WASM operations by total time - const wasmAgg = new Map(); - for (const r of report.records.filter(r => r.category === 'wasm')) { - const entry = wasmAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; - entry.count++; - entry.totalMs += r.duration; - if (r.duration > entry.maxMs) entry.maxMs = r.duration; - wasmAgg.set(r.name, entry); - } - const topWasm = [...wasmAgg.entries()] - .sort((a, b) => b[1].totalMs - a[1].totalMs) - .slice(0, 15); - - // Top RPC calls - const rpcAgg = new Map(); - for (const r of report.records.filter(r => r.category === 'rpc')) { - const entry = rpcAgg.get(r.name) ?? { count: 0, totalMs: 0, maxMs: 0 }; - entry.count++; - entry.totalMs += r.duration; - if (r.duration > entry.maxMs) entry.maxMs = r.duration; - rpcAgg.set(r.name, entry); - } - const topRpc = [...rpcAgg.entries()] - .sort((a, b) => b[1].totalMs - a[1].totalMs) - .slice(0, 15); + 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 tableStyle = { fontSize: 10, color: 'rgba(255,255,255,0.6)', fontFamily: 'monospace', py: 0.25, px: 1 }; + const SectionTitle = ({ children }: { children: string }) => ( + + {children} + + ); return ( - {/* Category breakdown */} - - BY CATEGORY - + 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)} + {cat} + {count} + {fmt(totalMs)} ))} - {/* Circuit executions */} {allSim.length > 0 && ( - - CIRCUIT EXECUTIONS - - - - {allSim.map(([name, { count, totalMs, maxMs }]) => ( - - {name} - ×{count} - {fmt(totalMs)} - max {fmt(maxMs)} - - ))} - - + CIRCUIT EXECUTIONS + )} - {/* Top WASM ops */} {topWasm.length > 0 && ( - - TOP WASM - - - - {topWasm.map(([name, { count, totalMs, maxMs }]) => ( - - {name} - ×{count} - {fmt(totalMs)} - max {fmt(maxMs)} - - ))} - - + TOP WASM + )} - {/* Top RPC calls */} {topRpc.length > 0 && ( - - TOP RPC - - - - {topRpc.map(([name, { count, totalMs, maxMs }]) => ( - - {name} - ×{count} - {fmt(totalMs)} - max {fmt(maxMs)} - - ))} - - + TOP RPC + )} ); } -// ─── Full-screen profile page (portal) ────────────────────────────────────── +// ─── Full-screen profile page ──────────────────────────────────────────────── function ProfilePage({ report, onClose }: { report: ProfileReport; onClose: () => void }) { const [minDuration, setMinDuration] = useState(0.5); @@ -877,20 +914,16 @@ function ProfilePage({ report, onClose }: { report: ProfileReport; onClose: () = borderBottom: '1px solid rgba(212,255,40,0.15)', backgroundColor: 'rgba(10,10,18,0.98)', }}> - PROFILE \u2014 {report.name} \u2014 {fmt(report.durationMs)} + PROFILE — {report.name} — {fmt(report.durationMs)} - {/* Min duration filter */} min {fmt(minDuration)} - setMinDuration(v as number)} sx={{ width: 100, color: 'rgba(212,255,40,0.5)', '& .MuiSlider-thumb': { width: 10, height: 10 } }} /> @@ -900,8 +933,7 @@ function ProfilePage({ report, onClose }: { report: ProfileReport; onClose: () = sx={{ fontSize: 10, py: 0.25, minWidth: 0, color: 'rgba(255,255,255,0.5)' }}> JSON - @@ -920,26 +952,22 @@ function ProfilePage({ report, onClose }: { report: ProfileReport; onClose: () = // ─── Main panel — compact pill ────────────────────────────────────────────── export function ProfilePanel() { - const [enabled] = useState(() => new URLSearchParams(location.search).has('profile')); const [recording, setRecording] = useState(false); const [report, setReport] = useState(null); const [fullscreen, setFullscreen] = useState(false); const instrumentedRef = useRef(false); const { wallet } = useWallet(); - // Install global interceptors on mount useEffect(() => { - if (!enabled) return; profiler.install(); - }, [enabled]); + }, []); - // Instrument wallet when available useEffect(() => { - if (!enabled || !wallet || instrumentedRef.current) return; + if (!wallet || instrumentedRef.current) return; profiler.instrumentWallet(wallet); instrumentedRef.current = true; console.info('[profiler] Wallet instrumented'); - }, [enabled, wallet]); + }, [wallet]); const handleToggle = useCallback(() => { if (recording) { @@ -954,8 +982,6 @@ export function ProfilePanel() { } }, [recording]); - if (!enabled) return null; - 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)' } }} /> 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 index 26f70df..cb3ae3b 100644 --- a/src/profiling/index.ts +++ b/src/profiling/index.ts @@ -4,43 +4,45 @@ * 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 (fetch, WASM) + * 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'; - -// ─── Types ─────────────────────────────────────────────────────────────────── +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 = 'wallet' | 'pxe' | 'sim' | 'oracle' | 'node' | 'rpc' | 'wasm'; - -export interface ProfileRecord { - name: string; - category: Category; - start: number; // ms from recording origin - duration: number; // ms - detail?: string; - error?: boolean; -} - -export interface ProfileReport { - name: string; - startedAt: number; // Date.now() at recording start - durationMs: number; - records: ProfileRecord[]; -} +export type { Category, ProfileRecord, ProfileReport } from './types'; // ─── Method wrapping ───────────────────────────────────────────────────────── -// Methods to skip — internal noise, getters, or things that break if wrapped. +// Methods to skip — internal plumbing, getters, or things that break if wrapped. const SKIP = new Set([ - 'constructor', 'toString', 'toJSON', 'valueOf', - 'then', // wrapping 'then' would break Promise detection - 'log', // logger getter + // 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', ]); /** @@ -73,6 +75,7 @@ function wrapAllMethods( ): () => void { const restores: (() => void)[] = []; const methods = collectMethods(target); + const wrappedNames: string[] = []; for (const name of methods) { const original = target[name]; @@ -80,34 +83,26 @@ function wrapAllMethods( const wrapped = function (this: any, ...args: any[]) { if (!profiler.isRecording) return original.apply(this, args); - const t0 = performance.now(); - let result: any; - try { - result = original.apply(this, args); - } catch (e) { - profiler.record(name, category, t0, performance.now() - t0, undefined, true); - throw e; - } - if (result && typeof result.then === 'function') { - return result.then( - (v: any) => { - profiler.record(name, category, t0, performance.now() - t0); - return v; - }, - (e: any) => { - profiler.record(name, category, t0, performance.now() - t0, undefined, true); - throw e; - }, - ); - } - profiler.record(name, category, t0, performance.now() - t0); - return result; + // 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; - target[name] = wrapped; - restores.push(() => { target[name] = original; }); + 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()); } @@ -121,12 +116,25 @@ class Profiler { 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. */ + /** + * 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, @@ -134,8 +142,10 @@ class Profiler { detail?: string, error?: boolean, ) { - if (!this._recording) return; + if (generation !== this._generation) return; this._records.push({ + id, + parentId, name, category, start: startAbsolute - this._origin, @@ -145,32 +155,122 @@ class Profiler { }); } - /** Install global interceptors (fetch, bb.js WASM). Call once, before wallet creation ideally. */ + /** + * 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; - const recFn = this.record.bind(this); - const isRec = () => this._recording; - this._cleanups.push(installFetchInterceptor(recFn, isRec)); - this._cleanups.push(await installWasmInterceptor(recFn, isRec)); + 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. Call once per wallet. */ + /** 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)); - // Patch circuit simulator prototypes (ACVM witness generation) - const recFn = this.record.bind(this); - const isRec = () => this._recording; - this._cleanups.push(installSimulatorInterceptorFromPXE(pxe, recFn, isRec)); + + if (pxe.simulator) wrapped.add(pxe.simulator); + this._cleanups.push(installSimulatorInterceptorFromPXE(pxe, this)); + + this.instrumentPxeInternals(pxe, wrapped); } + } - const node = wallet.aztecNode; - if (node) { - this._cleanups.push(wrapAllMethods(node, 'node', this)); + /** Walk PXE properties and wrap methods on internal services/stores. */ + private instrumentPxeInternals(pxe: any, alreadyWrapped: Set) { + for (const key of Object.getOwnPropertyNames(pxe)) { + if (key.startsWith('_') || key === 'log') continue; + let value: any; + try { value = pxe[key]; } catch { continue; } + if (!value || typeof value !== 'object' || alreadyWrapped.has(value)) continue; + + const methods = collectMethods(value); + if (methods.length === 0) continue; + + alreadyWrapped.add(value); + this._cleanups.push(wrapAllMethods(value, 'store', this)); } } @@ -180,8 +280,11 @@ class Profiler { this._origin = performance.now(); this._startedAt = Date.now(); this._records = []; + this._generation++; this._recording = true; - console.info(`[profiler] Started: "${name}"`); + console.info( + `[profiler] Started: "${name}" — zone tracking: ${zoneAvailable() ? 'on' : 'OFF (every span will be a root)'}`, + ); } stop(): ProfileReport { @@ -190,12 +293,11 @@ class Profiler { } this._recording = false; const durationMs = performance.now() - this._origin; - const records = this.deduplicateBatchedCalls([...this._records]); const report: ProfileReport = { name: this._name, startedAt: this._startedAt, durationMs, - records, + records: [...this._records], }; console.info( `[profiler] Stopped: "${this._name}" — ${(durationMs / 1000).toFixed(2)}s, ` + @@ -204,37 +306,6 @@ class Profiler { return report; } - /** - * When the node client batches multiple calls into one fetch, we capture - * both the individual node method spans AND the batch rpc span — same - * timing, redundant visual noise. Remove the individual node/rpc records - * that are covered by a batch. - */ - private deduplicateBatchedCalls(records: ProfileRecord[]): ProfileRecord[] { - const batches = records.filter( - r => r.category === 'rpc' && r.name.startsWith('[batch]'), - ); - if (batches.length === 0) return records; - - return records.filter(r => { - // Only deduplicate node-level and individual rpc records - if (r.category !== 'node' && r.category !== 'rpc') return true; - // Keep batch records themselves - if (r.name.startsWith('[batch]')) return true; - - const rEnd = r.start + r.duration; - - for (const batch of batches) { - const batchEnd = batch.start + batch.duration; - // Check timing overlap (the node call and batch should overlap) - if (batch.start > rEnd || batchEnd < r.start) continue; - // Check if the batch label mentions this method name - if (batch.name.includes(r.name)) return false; - } - return true; - }); - } - download(report: ProfileReport) { const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); @@ -253,3 +324,4 @@ class Profiler { } export const profiler = new Profiler(); +export type { Profiler }; diff --git a/src/profiling/interceptors.ts b/src/profiling/interceptors.ts index ec72c4c..1265d62 100644 --- a/src/profiling/interceptors.ts +++ b/src/profiling/interceptors.ts @@ -1,81 +1,63 @@ /** - * Fetch + WASM interception for profiling. + * Fetch + WASM + simulator + standalone-function interception for profiling. * - * Fetch: patches window.fetch to extract JSON-RPC method names and record timing. - * WASM: patches BarretenbergSync/Barretenberg backend.call to decode msgpack - * operation names and record timing for every bb.js API call. + * 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`. */ -// ─── Callback interface ───────────────────────────────────────────────────── +import type { Profiler } from './index'; +import { findAncestorSpan } from './context'; -export type RecordFn = ( - name: string, - category: 'rpc' | 'wasm' | 'sim' | 'oracle', - startAbsolute: number, - duration: number, - detail?: string, - error?: boolean, -) => void; - -export type IsRecording = () => boolean; +// 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( - record: RecordFn, - isRecording: IsRecording, -): () => void { +export function installFetchInterceptor(profiler: Profiler): () => void { const original = window.fetch.bind(window); - window.fetch = async ( + window.fetch = ( input: RequestInfo | URL, init?: RequestInit, ): Promise => { - const recording = isRecording(); - if (!recording) return original(input, init); - - const url = - typeof input === 'string' - ? input - : input instanceof URL - ? input.href - : input.url; - const t0 = performance.now(); - - // Extract JSON-RPC method name(s) from request body + 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; - if (init?.body && typeof init.body === 'string') { - 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 { - /* not JSON */ - } - } - if (!method) { - try { - method = new URL(url, location.href).pathname; - } catch { - method = url; + 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; - try { - const response = await original(input, init); - record(label, 'rpc', t0, performance.now() - t0, `${response.status}`); - return response; - } catch (e) { - record(label, 'rpc', t0, performance.now() - t0, 'network error', true); - throw e; - } + // 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 () => { @@ -122,8 +104,7 @@ function decodeMsgpackOpName(buf: Uint8Array): string | null { function wrapBackendCall( backend: any, - record: RecordFn, - isRecording: IsRecording, + profiler: Profiler, isSync: boolean, ): () => void { if (!backend || typeof backend.call !== 'function' || backend.call.__profiled) @@ -133,40 +114,25 @@ function wrapBackendCall( if (isSync) { backend.call = function (inputBuffer: Uint8Array) { - if (!isRecording()) return original(inputBuffer); + if (!profiler.isRecording) return original(inputBuffer); const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_sync'; - const t0 = performance.now(); - const result = original(inputBuffer); - record(opName, 'wasm', t0, performance.now() - t0); - return result; + return profiler.runSpan(opName, 'wasm', () => original(inputBuffer)); }; } else { - backend.call = async function (inputBuffer: Uint8Array) { - if (!isRecording()) return original(inputBuffer); + backend.call = function (inputBuffer: Uint8Array) { + if (!profiler.isRecording) return original(inputBuffer); const opName = decodeMsgpackOpName(inputBuffer) ?? 'bb_async'; - const t0 = performance.now(); - try { - const result = await original(inputBuffer); - record(opName, 'wasm', t0, performance.now() - t0); - return result; - } catch (err) { - record(opName, 'wasm', t0, performance.now() - t0, undefined, true); - throw err; - } + return profiler.runSpan(opName, 'wasm', () => original(inputBuffer)); }; } backend.call.__profiled = true; - const restore = () => { + return () => { backend.call = original; }; - return restore; } -export async function installWasmInterceptor( - record: RecordFn, - isRecording: IsRecording, -): Promise<() => void> { +export async function installWasmInterceptor(profiler: Profiler): Promise<() => void> { const restores: (() => void)[] = []; try { @@ -176,26 +142,20 @@ export async function installWasmInterceptor( // Patch BarretenbergSync (main-thread hashing: poseidon, pedersen, etc.) if (BBSync) { - // Wrap existing singleton if already initialized try { const existing = BBSync.getSingleton(); if (existing?.backend) - restores.push( - wrapBackendCall(existing.backend, record, isRecording, true), - ); + restores.push(wrapBackendCall(existing.backend, profiler, true)); } catch { /* not yet init'd */ } - // Wrap future singletons 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, record, isRecording, true), - ); + restores.push(wrapBackendCall(inst.backend, profiler, true)); return inst; }; BBSync.initSingleton.__profiled = true; @@ -210,9 +170,7 @@ export async function installWasmInterceptor( try { const existing = BB.getSingleton(); if (existing?.backend) - restores.push( - wrapBackendCall(existing.backend, record, isRecording, false), - ); + restores.push(wrapBackendCall(existing.backend, profiler, false)); } catch { /* not yet init'd */ } @@ -222,9 +180,7 @@ export async function installWasmInterceptor( BB.initSingleton = async (...args: any[]) => { const inst = await orig(...args); if (inst?.backend) - restores.push( - wrapBackendCall(inst.backend, record, isRecording, false), - ); + restores.push(wrapBackendCall(inst.backend, profiler, false)); return inst; }; BB.initSingleton.__profiled = true; @@ -240,54 +196,16 @@ export async function installWasmInterceptor( return () => restores.forEach((r) => r()); } -// ─── Simulator interceptor ────────────────────────────────────────────────── -// Patches prototype methods on the circuit simulator (ACVM witness generation) -// reached through the PXE instance — no problematic imports needed. - -function wrapProtoMethod( - proto: any, - method: string, - record: RecordFn, - isRecording: IsRecording, - label: (args: any[]) => string, -): () => void { - const original = proto[method]; - if (typeof original !== 'function' || original.__profiled) return () => {}; - - proto[method] = function (this: any, ...args: any[]) { - if (!isRecording()) return original.apply(this, args); - const name = label(args); - const t0 = performance.now(); - let result: any; - try { - result = original.apply(this, args); - } catch (e) { - record(name, 'sim', t0, performance.now() - t0, undefined, true); - throw e; - } - if (result && typeof result.then === 'function') { - return result.then( - (v: any) => { record(name, 'sim', t0, performance.now() - t0); return v; }, - (e: any) => { record(name, 'sim', t0, performance.now() - t0, undefined, true); throw e; }, - ); - } - record(name, 'sim', t0, performance.now() - t0); - return result; - }; - proto[method].__profiled = true; - return () => { proto[method] = original; }; -} +// ─── Simulator + oracle callback interceptor ─────────────────────────────── /** * Wrap every method on an ACIRCallback (oracle) object with profiling. - * The callback is Record Promise<...>>. - * Each key is an oracle function name (getNotes, getPublicDataTreeWitness, etc.). + * 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, - record: RecordFn, - isRecording: IsRecording, -): any { +function wrapOracleCallback(callback: any, profiler: Profiler): any { if (!callback || typeof callback !== 'object') return callback; const wrapped: any = {}; @@ -297,17 +215,9 @@ function wrapOracleCallback( wrapped[key] = original; continue; } - wrapped[key] = async function (...args: any[]) { - if (!isRecording()) return original.apply(this, args); - const t0 = performance.now(); - try { - const result = await original.apply(this, args); - record(key, 'oracle', t0, performance.now() - t0); - return result; - } catch (e) { - record(key, 'oracle', t0, performance.now() - t0, undefined, true); - throw e; - } + wrapped[key] = function (...args: any[]) { + if (!profiler.isRecording) return original.apply(this, args); + return profiler.runSpan(key, 'oracle', () => original.apply(this, args)); }; } return wrapped; @@ -320,14 +230,12 @@ function wrapOracleCallback( * * Patches: * - executeUserCircuit: records the circuit execution + wraps the oracle - * callback so every oracle call (getNotes, getPublicDataTreeWitness, etc.) - * gets its own span. + * callback so every oracle call gets its own span. * - executeProtocolCircuit: records protocol circuit execution. */ export function installSimulatorInterceptorFromPXE( pxe: any, - record: RecordFn, - isRecording: IsRecording, + profiler: Profiler, ): () => void { const restores: (() => void)[] = []; @@ -337,53 +245,37 @@ export function installSimulatorInterceptorFromPXE( const simProto = Object.getPrototypeOf(sim); if (!simProto) return () => {}; - // executeUserCircuit(input, artifact, callback) - // We wrap the method AND the oracle callback (3rd arg). if (typeof simProto.executeUserCircuit === 'function' && !simProto.executeUserCircuit.__profiled) { const original = simProto.executeUserCircuit; - simProto.executeUserCircuit = async function (this: any, input: any, artifact: any, callback: any, ...rest: any[]) { - if (!isRecording()) return original.call(this, input, artifact, callback, ...rest); - + 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, record, isRecording); - - const t0 = performance.now(); - try { - const result = await original.call(this, input, artifact, wrappedCallback, ...rest); - record(label, 'sim', t0, performance.now() - t0); - return result; - } catch (e) { - record(label, 'sim', t0, performance.now() - t0, undefined, true); - throw e; - } + 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; }); } - // executeProtocolCircuit(input, artifact, callback) if (typeof simProto.executeProtocolCircuit === 'function' && !simProto.executeProtocolCircuit.__profiled) { const original = simProto.executeProtocolCircuit; - simProto.executeProtocolCircuit = async function (this: any, input: any, artifact: any, callback: any, ...rest: any[]) { - if (!isRecording()) return original.call(this, input, artifact, callback, ...rest); - + 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'; - // Protocol circuits also get oracle callback wrapping (for ForeignCallHandler) const wrappedCallback = callback && typeof callback === 'object' - ? wrapOracleCallback(callback, record, isRecording) + ? wrapOracleCallback(callback, profiler) : callback; - - const t0 = performance.now(); - try { - const result = await original.call(this, input, artifact, wrappedCallback, ...rest); - record(label, 'sim', t0, performance.now() - t0); - return result; - } catch (e) { - record(label, 'sim', t0, performance.now() - t0, undefined, true); - throw e; - } + return profiler.runSpan(label, 'sim', () => + original.call(this, input, artifact, wrappedCallback, ...rest), + ); }; simProto.executeProtocolCircuit.__profiled = true; restores.push(() => { simProto.executeProtocolCircuit = original; }); @@ -391,3 +283,11 @@ export function installSimulatorInterceptorFromPXE( 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/vite.config.ts b/vite.config.ts index 6cdf4c4..a628fe6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -93,18 +93,28 @@ 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: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp', - // Required for the JS Self-Profiling API (in-page sampling profiler) - 'Document-Policy': 'js-profiling', }, fs: { allow: [searchForWorkspaceRoot(process.cwd())], @@ -113,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..2ddc7cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6126,6 +6126,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 +9842,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 From 24dd5e32443bc56a5c2221830a721af13051ff02 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 16 Apr 2026 06:19:08 +0200 Subject: [PATCH 3/6] unify contract handling --- src/components/swap/SwapContainer.tsx | 5 +- src/contexts/contracts/ContractsContext.tsx | 30 +++++++++-- src/contexts/contracts/reducer.ts | 3 ++ src/contexts/swap/SwapContext.tsx | 4 +- src/hooks/useSubscriptionStatus.ts | 13 ++--- src/services/contractService.ts | 58 ++++++++++----------- 6 files changed, 68 insertions(+), 45 deletions(-) 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/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/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; From 28641b1761fef37728ca342da49b31dc09f341e8 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 16 Apr 2026 10:14:22 +0200 Subject: [PATCH 4/6] update --- contracts/amm/Nargo.toml | 6 +- contracts/proof_of_password/Nargo.toml | 8 +- package.json | 26 +- scripts/update.js | 159 ++++--- src/components/ProfilePanel.tsx | 27 +- src/config/networks/testnet.json | 32 +- yarn.lock | 566 ++++++++++++------------- 7 files changed, 428 insertions(+), 396 deletions(-) diff --git a/contracts/amm/Nargo.toml b/contracts/amm/Nargo.toml index c7c7d0f..dbadd9e 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.20260416", directory = "noir-projects/aztec-nr/aztec" } +token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } +uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", 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..750b5fc 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.20260416", directory = "noir-projects/aztec-nr/aztec" } +token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", 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.20260416", directory = "noir-projects/aztec-nr/compressed-string" } \ No newline at end of file diff --git a/package.json b/package.json index 6fdc890..875f80d 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.20260416", + "@aztec/aztec.js": "v4.3.0-nightly.20260416", + "@aztec/constants": "v4.3.0-nightly.20260416", + "@aztec/entrypoints": "v4.3.0-nightly.20260416", + "@aztec/foundation": "v4.3.0-nightly.20260416", + "@aztec/noir-contracts.js": "v4.3.0-nightly.20260416", + "@aztec/protocol-contracts": "v4.3.0-nightly.20260416", + "@aztec/pxe": "v4.3.0-nightly.20260416", + "@aztec/stdlib": "v4.3.0-nightly.20260416", + "@aztec/wallet-sdk": "v4.3.0-nightly.20260416", "@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.16", + "@gregojuice/embedded-wallet": "^0.0.16", "@mui/icons-material": "^6.3.1", "@mui/material": "^6.3.1", "@mui/styles": "^6.3.1", @@ -52,7 +52,7 @@ "zone.js": "^0.16.1" }, "devDependencies": { - "@aztec/wallets": "v4.2.0-nightly.20260412", + "@aztec/wallets": "v4.3.0-nightly.20260416", "@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/components/ProfilePanel.tsx b/src/components/ProfilePanel.tsx index cba5e28..4003857 100644 --- a/src/components/ProfilePanel.tsx +++ b/src/components/ProfilePanel.tsx @@ -371,16 +371,18 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur const labelX = 4 + indent; const cy = y + ROW_H / 2; - // Collapse triangle + // Expand/collapse triangle — bright so it's clearly clickable. if (item.hasChildren) { const isExp = expanded.has(item.nodeId); - ctx.fillStyle = 'rgba(255,255,255,0.55)'; + 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); @@ -471,13 +473,30 @@ function WaterfallChart({ report, minDuration }: { report: ProfileReport; minDur 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) + 2, y, Math.min(chartViewportW, x + w) - Math.max(0, x) - 4, ROW_H); + 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) + 3, y + ROW_H - 6); + ctx.fillText(`${r.name} ${fmt(r.duration)}`, Math.max(0, x) + textOffset, y + ROW_H - 6); ctx.restore(); } } diff --git a/src/config/networks/testnet.json b/src/config/networks/testnet.json index c083900..ad58507 100644 --- a/src/config/networks/testnet.json +++ b/src/config/networks/testnet.json @@ -4,28 +4,16 @@ "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", - "subscriptionFPC": { - "address": "0x2615b16dd7a23615145f889975cecc4a70beff5e799eef2b70bab329b4aa6fbc", - "secretKey": "0x074cd289d45081a02dbc0e7339705e0e6faa4a34bbc7e9d0b90363e434c2f044", - "functions": { - "0x10c70f9633d9f97d1e25686f90082e54b5bc22cee82679e3ebd3ad92bbe83c56": { - "0xa539bd29": 0 - }, - "0x18dbac86293e725cc506fce4385eafb0d3ab2d5b8c46b92bfb333bb13599aa56": { - "0xfd228669": 0 - } - } - } -} + "deployedAt": "2026-04-16T07:16:45.008Z" +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2ddc7cc..e71ae20 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.20260416, @aztec/accounts@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/accounts@npm:4.3.0-nightly.20260416" + dependencies: + "@aztec/aztec.js": "npm:4.3.0-nightly.20260416" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" - checksum: 10c0/4c309f8391993139d4a5648e9f700901a5dd11fdf69676c7bea772f085314699effd33f259706814c9cedb5cd4b5065dc1f3c1ce1534e8524230c00c0c70c2ed + checksum: 10c0/745a6d415d91828e337dd96a53f3c42f9ea1c88ccd6c9799e53013d8a1460189fda0e8788d49865245630dabcfdfd2f909a5398390df4983fe11763320591e3c 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.20260416, @aztec/aztec.js@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/aztec.js@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" 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/a70d1431d631eaf878046505719ba5cee020f9db97a29e066bb572de35fca6d697527f00392a9122ca744615f7e00ae8c403a8c9bd18cfb2521b040b5876af8d 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/bb-prover@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + "@aztec/simulator": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" + "@aztec/world-state": "npm:4.3.0-nightly.20260416" 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/c7e86a7b28953a9cd9cf2afc6a0fd012ff99db750a1bb59c26df960266096e337956a8ec469da44ee9fa4614aac2b92670b6a47507aeeaa66e7bb0eff9e296fc 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/bb.js@npm:4.3.0-nightly.20260416" 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/746906ed9688ac09981087f4fc4406c00819c5ff10087c9efbd12e153e431180be9275da5d8bca8c84348184ae19cab1b513a7e63ef7531652993c6bfe49f7fa 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/blob-lib@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/constants": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" "@crate-crypto/node-eth-kzg": "npm:^0.10.0" tslib: "npm:^2.4.0" - checksum: 10c0/fe8b19a8bb26970cae58876b705ee3101790ac10041fef2f2d6b9b9acef6ea5986316704bb187307c05bd78b59cc06147f49bf1042adead24142e6c4b631c543 + checksum: 10c0/e83734b6b64d2d955ecce8bb1a28d205238fa2dbd234883f7e24093897bba924414f427c305c2fbd19cd05629d1377fe72bfa8a9f347a7685a56542ad1711db2 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/builder@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" commander: "npm:^12.1.0" - checksum: 10c0/5488207fde2ab772e360e0a8ace186917c684002da01d48fcaa03f28fd82c0ffb9e64d3d686f406d02486e0703138695d73483e4d3302fe6c8b9435ab47ab685 + checksum: 10c0/9f26fd350660171c98773f2fb62967a96be4f7cea1816f83123b39c6a19547fb562e5e33ea32c72fd066342b67e18ace6139e7b12105c7d36ad9ab7a9f7b0568 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.20260416, @aztec/constants@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/constants@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" - checksum: 10c0/cfec60215306956d536ded2fb285ee65bed9a4062c61f76dc8cb6dc7b6efd2501091ef5df7f699f8b5a6df3493febaba781f5a8ad4cab2ca4547cca322511322 + checksum: 10c0/c0121fe6e9c79e35641a4f80ea8a01a8082d4e18550aaa8e8a1f7d95430241c72fce30e58dbaaf48da598b3087dd7ae92de4b95bfd4233f5bcc6d8cdc4119149 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.20260416, @aztec/entrypoints@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/entrypoints@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/12383a50331cfce063c2cd02477ff69f85bcbf9b7d81fa44e0eda6b73107819b1cd8d4bb3312926e4f6662c1f640f39c3ca5c5a07df20559a12ce23dcbbb03cf + checksum: 10c0/eb0d6aa6590af77c0a985b587368ba7d8ddb47d47c7966d1a8361150e6a80204631295486271a0bd5cd48d49487b0a7d1c45e2e2dc255a6cf10c904b2a09f30c 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/ethereum@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" "@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/bc232e54e7a23be3cc68e841518a205d26a78a5c9788bb8eb23bdb79877b5ab0d5b31966062349dcd4d966fb1b70d85a1c6dea58fcd74bb42b1c0e7efe553177 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.20260416, @aztec/foundation@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/foundation@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/bb.js": "npm:4.3.0-nightly.20260416" "@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/ced8edaa8e387cf0076cc759e1e82d02d3719ccec01c74448e9149996d0ec5aec906032f0db7e1fc1cc0c5c1134c0107ea356b8fa81d1e1730568f1bb11d99e4 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/key-store@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" - checksum: 10c0/84e34402e3d7490d2a0c2c005aca57834c1b1ecbb621bd91e3a4e398cafe04617da109124757daae651ab2720e86f1f258267f9987303a10f804b5d61b37513d + checksum: 10c0/34e0592dcf6f5077eb26a209c8219aa88199e8dbb75432f76e592fed6ff15d47fe21f794848f8f891a4217b4026f2efdc1594b1a224720b2e9b2b174cdc2b9d8 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/kv-store@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/native": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" 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/2131efeffad61cdb3913bf46e59f48db42ccf17dbd6a99d18d55cc34d7812b7fafed9d765965904b5487446236e01e3f6d63a2dd5694847014815f5c7ae784a1 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/l1-artifacts@npm:4.3.0-nightly.20260416" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/209dd1b1e9cf42da472a866415a2c4d8191dc0e5209eec406c253f4100b7c1d3aaa28e96cf5c7fab7aab1670dd8b98a7cd3ab3631fa62ef14f0a5394652faa1c + checksum: 10c0/b418d92180c4511b5f68db36060c64c8b5f58c72b8b728d7a4a906b1de13d2bdd5120cea6d5a848de12f8454571773f01206181782f0a88e82b395386760d2c2 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/merkle-tree@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" sha256: "npm:^0.2.0" tslib: "npm:^2.4.0" - checksum: 10c0/f8c6c0fae86589d216f38780d46caceb678f8f9a6e3ace3a43eff00f9840246b8712854d0c126233f2c5364983635bb382333e4a505f63759b4834206f3c9593 + checksum: 10c0/1c40422b2b6d8da7b133f94597438df0dcf2a50912be4e0e7fa4f3a895317f75f902b103d63c34636c732c471d8d0ef1528eaa2770884f4315056e28b266c4a1 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/native@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" msgpackr: "npm:^1.11.2" - checksum: 10c0/32ad54727e85a4fed81707022b98940e42b15a3858c36cadbbb6429f07b98ebf9c3fc0f81bb7d3b112536f97458597f7005c04aa79a8a26d147ff38a2b469e06 + checksum: 10c0/7e4f93c1da13487e3577af5857b4d54cfe0293bdb867a4ee351c8ed90afc77aa9a7fb1643936c2c869f64d504a371b3a3974d6dffc5dae587ca93da6fdfda633 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-acvm_js@npm:4.3.0-nightly.20260416" + checksum: 10c0/8013baf3075d0dfe3eed001eafedf0c01b1bb4096fd989438f92d71023e9d91ca8a4d69df53c7157d657bcd2a22e7702d80a3f96966465fda9f877bff191569b 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-contracts.js@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" + "@aztec/aztec.js": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" - checksum: 10c0/ed8e7fdf736232dd8a6b3abac0ee6d47c68acb32305df47e916f4537b4aa52d26aa6a8de7b25d676275077114e20a4a90f9ca294280582a0828d1a359b605c84 + checksum: 10c0/6e3b864cccb1b551887d7069d47cac4ad41e7904b8a5ac68627bdae7648942111ca271f8f7dd6de5803f4bc59241d511760bde2ea049be80ad5751f726d5df3e 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-noir_codegen@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/noir-types": "npm:4.2.0-nightly.20260412" - glob: "npm:^13.0.0" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + glob: "npm:^13.0.6" ts-command-line-args: "npm:^2.5.1" bin: noir-codegen: lib/main.js - checksum: 10c0/31b7aedfb8f8403660462ceff20ec31c3ac69b9723ea9dd52b0edb13c17ea657f9b9b2b9e64efe2009eaa9934c9bc5c711e4409a9ffe975d3a12a69209255d39 + checksum: 10c0/97f6c7ee215e44be9d37f1d233a4337c3025acab49de6471e5b4d907ddc61b33b131eed82945f58330544872f9e6883f7565571ce7ef76f5db30dfe20b4bee82 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-noirc_abi@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/noir-types": "npm:4.2.0-nightly.20260412" - checksum: 10c0/22c0d8652e2bb0e1eea3da5942349c944c4b438b3384b28002afcd4e6c0852e1d57855ec2432a5bdbf51bcbef6514dbac2e1f3ecddfa2c7392f90769f49d1523 + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + checksum: 10c0/a9712c9d950c7b31eaaa302984124ed6a6dd8c805480decc667a4379f53091076975cd8d4aa69cd1c9a3feb9aed486e050ddfb3b090bb7aaeb094313b90c217c 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-protocol-circuits-types@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260416" + "@aztec/noir-noir_codegen": "npm:4.3.0-nightly.20260416" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" change-case: "npm:^5.4.4" tslib: "npm:^2.4.0" - checksum: 10c0/d9a21827f9c4cb57e8065db77bf745750dc80c89217e9e94eb116909be187bbd0aea0a30ce7bfcb34eb7f226e2a28902af62e94b5ed088982e27d023a53d644a + checksum: 10c0/1a38d8f3a7d884a2575a67565945cfee32b30370e69182fd5fdcd254ae67e856a34536bab187c1acd8a14c5be2f4aaf75ec1385c3a07d31dd29ca8ea2911f52e 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/noir-types@npm:4.3.0-nightly.20260416" + checksum: 10c0/15968ec7e077fc9e011a3bb51d83e44cccd46e1b8db6e3e6cfcf4763cada5a3d8ea99f84eb1d7eaae3e8252fa83df0f92c39d95044f0e10db4660bae28d3b385 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.20260416, @aztec/protocol-contracts@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/protocol-contracts@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" 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/42fa4fd67f87f07025fdec98455999a7f67464339736b37f8ebc582833ba7c8d4a1d1278bc6e2430c0f1db5893686fa9064f076994e7fb5122a92bf5d26ca382 + languageName: node + linkType: hard + +"@aztec/pxe@npm:4.3.0-nightly.20260416, @aztec/pxe@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/pxe@npm:4.3.0-nightly.20260416" + dependencies: + "@aztec/bb-prover": "npm:4.3.0-nightly.20260416" + "@aztec/bb.js": "npm:4.3.0-nightly.20260416" + "@aztec/builder": "npm:4.3.0-nightly.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/key-store": "npm:4.3.0-nightly.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/simulator": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" 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/9616043cd053a6db25e2026100cf2fb06d01cd3f18076b9d6da74f42a3432504ace78efcf0200ed32a69834c24a459af07f8544f3e1e3cd507e96f74e6adc8e0 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/simulator@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/native": "npm:4.3.0-nightly.20260416" + "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260416" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" + "@aztec/world-state": "npm:4.3.0-nightly.20260416" lodash.clonedeep: "npm:^4.5.0" lodash.merge: "npm:^4.6.2" tslib: "npm:^2.4.0" - checksum: 10c0/6c09b23f76ea3327e23f43cccc51aff6fca52599f755180173bdd170a116e25ee28cc89bd3b88b6ba0920256b0de3ff205007d0df4b7624717c4c9f3f0dd2c0d + checksum: 10c0/abf62dba2e38c5c1a554b30d3cf5c1b3e9a9aef4702fc25e3726d273f8e0fad9478585074f31e23f6e96328bd08724d2332c23dc954b11acfd5577263eab76fc 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.20260416, @aztec/stdlib@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/stdlib@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/blob-lib": "npm:4.3.0-nightly.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" + "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" + "@aztec/validator-ha-signer": "npm:4.3.0-nightly.20260416" "@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/b3deccd1fa6f0028c15e1c895d88487740e6497b6f1332742f656b21db06f904524cd58c13b93f48ebe194673fd980b6bb62e91e2736072ec090d4ed23753264 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/telemetry-client@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/foundation": "npm:4.2.0-nightly.20260412" - "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" "@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/717220c7cdb69b7f0d570c8de75035520b4f978a2eab8de4e687bef6f7b87df344367c520526275f073529e2eee8f3ebf233825ea5fc6d976d2666c464767c58 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/validator-ha-signer@npm:4.3.0-nightly.20260416" dependencies: - "@aztec/ethereum": "npm:4.2.0-nightly.20260412" - "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" 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/403f876be2fb0783bcb2ab043e46e05bf4f9f273335d5f07070b8026833c2d9ab2726cac9f8246286d3905d24d1fc229c61cb8a20acae744e7378119ea8fba98 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.20260416, @aztec/wallet-sdk@npm:v4.3.0-nightly.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/wallet-sdk@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/pxe": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + checksum: 10c0/3ec66c35a202b9f5bd6f64330273a3408186f6d31544db7e33db88fd3309a45e3bdedebb8050c4d57e813ab4761a642682283b98e17e783dbb8689fdeb480c5c 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/wallets@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/aztec.js": "npm:4.3.0-nightly.20260416" + "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/pxe": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@aztec/wallet-sdk": "npm:4.3.0-nightly.20260416" + checksum: 10c0/dd35237d936944bcdbdda9d7be3ddd6fc4c360fa790c7b445ca53f19eb2df9e34d608a66156e9bb2372176b4218cc0a0cbfc666179df8536e9a5d08368c70c4f 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.20260416": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/world-state@npm:4.3.0-nightly.20260416" 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.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/merkle-tree": "npm:4.3.0-nightly.20260416" + "@aztec/native": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/71a72b1a8618e899fa5282a944e0d8b965635d20a3699fb124db57c13550a54afceeca7de31f6bd89e8cd5d90507f0d006e3e10ccbd1d520b3adf0e7a89c8458 + checksum: 10c0/00fda56b2d05f7523047b143fe3870a74a0dc90b26eff05a8893ecbf02447f81a5e45441ff0e4e35732b8e2d782f8b8bf96beb460e669a3320b730d08b5f59a3 languageName: node linkType: hard @@ -1773,30 +1773,30 @@ __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.16, @gregojuice/contracts@npm:^0.0.16": + version: 0.0.16 + resolution: "@gregojuice/contracts@npm:0.0.16" 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.20260416" + "@aztec/foundation": "npm:v4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" + "@aztec/wallets": "npm:v4.3.0-nightly.20260416" + checksum: 10c0/055ddbe4b9c3c946a5cce411dbcb8c20178ec2157d5608eb644a95f92f6288fdf41ead844b746b64dd5ac9128c5ba98dbb01708e74d6a07d96dcb8878e2893c6 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.16": + version: 0.0.16 + resolution: "@gregojuice/embedded-wallet@npm:0.0.16" 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.20260416" + "@aztec/entrypoints": "npm:v4.3.0-nightly.20260416" + "@aztec/foundation": "npm:v4.3.0-nightly.20260416" + "@aztec/pxe": "npm:v4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" + "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260416" + "@aztec/wallets": "npm:v4.3.0-nightly.20260416" + "@gregojuice/contracts": "npm:0.0.16" peerDependencies: "@emotion/react": ">=11" "@mui/icons-material": ">=6" @@ -1811,7 +1811,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/bf3782ac83b4ffd7726ae0ccae1a695e003a19f36fc90bd6f12d839a1dd27c49e180e8d74c64459b76a9e6d78dbbb77e5c1613f70449b98715e6163ef6f93537 + checksum: 10c0/e6f46f561021a1cd24daf4a144a928105e2e95028f9859daa574de84a425213356cdbbdde9dda0068b56186218473de65e7010a4dd34652d9fe556c4a632a2c9 languageName: node linkType: hard @@ -5999,7 +5999,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 +6086,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.20260416" + "@aztec/aztec.js": "npm:v4.3.0-nightly.20260416" + "@aztec/constants": "npm:v4.3.0-nightly.20260416" + "@aztec/entrypoints": "npm:v4.3.0-nightly.20260416" + "@aztec/foundation": "npm:v4.3.0-nightly.20260416" + "@aztec/noir-contracts.js": "npm:v4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:v4.3.0-nightly.20260416" + "@aztec/pxe": "npm:v4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" + "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260416" + "@aztec/wallets": "npm:v4.3.0-nightly.20260416" "@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.16" + "@gregojuice/embedded-wallet": "npm:^0.0.16" "@mui/icons-material": "npm:^6.3.1" "@mui/material": "npm:^6.3.1" "@mui/styles": "npm:^6.3.1" From e7ae43cbaa9972d8e8f597cf9dbcf446cab9a044 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 16 Apr 2026 10:55:55 +0200 Subject: [PATCH 5/6] patched --- .gitignore | 1 + ...pm-4.3.0-nightly.20260416-b01dc128ea.patch | 55 +++++++++++++++++++ package.json | 3 + src/config/networks/testnet.json | 14 ++++- yarn.lock | 31 ++++++++++- 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 .yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch 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/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch b/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch new file mode 100644 index 0000000..db4ca2f --- /dev/null +++ b/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch @@ -0,0 +1,55 @@ +diff --git a/dest/storage/capsule_store/capsule_store.js b/dest/storage/capsule_store/capsule_store.js +index f455fc151d506562fbf20aa5b13b269a30c981b1..dccd521181ed58ccef3a23da2418f4f7648d671f 100644 +--- a/dest/storage/capsule_store/capsule_store.js ++++ b/dest/storage/capsule_store/capsule_store.js +@@ -108,11 +108,16 @@ export class CapsuleStore { + this.#setOnStage(jobId, dbSlotKey, Buffer.concat(capsule.map((value)=>value.toBuffer()))); + } + /** +- * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. ++ * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. Wraps the DB read in ++ * a transactionAsync so it enters the serial queue and never observes a stale IndexedDB transaction set by a ++ * concurrent transactionAsync caller. + * @param contractAddress - The contract address under which the data is scoped. + * @param slot - The slot in the database to read. + * @returns The stored data or `null` if no data is stored under the slot. + */ async getCapsule(contractAddress, slot, jobId, scope) { ++ return this.#store.transactionAsync(()=>this.#getCapsuleInternal(contractAddress, slot, jobId, scope)); ++ } ++ /** Same as getCapsule but without its own transaction — for use inside an existing transactionAsync. */ async #getCapsuleInternal(contractAddress, slot, jobId, scope) { + const dataBuffer = await this.#getFromStage(jobId, dbSlotToKey(contractAddress, slot, scope)); + if (!dataBuffer) { + this.logger.trace(`Data not found for contract ${contractAddress.toString()} and slot ${slot.toString()}`); +@@ -182,7 +187,7 @@ export class CapsuleStore { + // and not using a transaction here would heavily impact performance. + return this.#store.transactionAsync(async ()=>{ + // Load current length, defaulting to 0 if not found +- const lengthData = await this.getCapsule(contractAddress, baseSlot, jobId, scope); ++ const lengthData = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); + const currentLength = lengthData ? lengthData[0].toNumber() : 0; + // Store each capsule at consecutive slots after baseSlot + 1 + currentLength + for(let i = 0; i < content.length; i++){ +@@ -204,12 +209,12 @@ export class CapsuleStore { + // of jobs: different calls running concurrently on the same contract may cause trouble. + return this.#store.transactionAsync(async ()=>{ + // Load length, defaulting to 0 if not found +- const maybeLength = await this.getCapsule(contractAddress, baseSlot, jobId, scope); ++ const maybeLength = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); + const length = maybeLength ? maybeLength[0].toBigInt() : 0n; + const values = []; + // Read each capsule at consecutive slots after baseSlot + for(let i = 0; i < length; i++){ +- const currentValue = await this.getCapsule(contractAddress, arraySlot(baseSlot, i), jobId, scope); ++ const currentValue = await this.#getCapsuleInternal(contractAddress, arraySlot(baseSlot, i), jobId, scope); + if (currentValue == undefined) { + throw new Error(`Expected non-empty value at capsule array in base slot ${baseSlot} at index ${i} for contract ${contractAddress}`); + } +@@ -229,7 +234,7 @@ export class CapsuleStore { + // of jobs: different calls running concurrently on the same contract may cause trouble. + return this.#store.transactionAsync(async ()=>{ + // Load current length, defaulting to 0 if not found +- const maybeLength = await this.getCapsule(contractAddress, baseSlot, jobId, scope); ++ const maybeLength = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); + const originalLength = maybeLength ? maybeLength[0].toNumber() : 0; + // Set the new length + this.setCapsule(contractAddress, baseSlot, [ diff --git a/package.json b/package.json index 875f80d..8741f6c 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,9 @@ "zod": "^3.23.8", "zone.js": "^0.16.1" }, + "resolutions": { + "@aztec/pxe": "patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch" + }, "devDependencies": { "@aztec/wallets": "v4.3.0-nightly.20260416", "@eslint/js": "^9.18.0", diff --git a/src/config/networks/testnet.json b/src/config/networks/testnet.json index ad58507..987b6a8 100644 --- a/src/config/networks/testnet.json +++ b/src/config/networks/testnet.json @@ -15,5 +15,17 @@ "deployer": { "address": "0x12952c3d8a4116782ee85b93c634afbd4cdf2beabedd8258e89737616c269c33" }, - "deployedAt": "2026-04-16T07:16:45.008Z" + "deployedAt": "2026-04-16T07:16:45.008Z", + "subscriptionFPC": { + "address": "0x029f91316f06e4d46e1d77b831a394dbe01676480e9114403838b8df944ca043", + "secretKey": "0x2bd6014457e941f1ee656a5824cd6016289acfbd708a9019860284c898c9461c", + "functions": { + "0x0cc4ceb7cf7cc514ea8fbc5dc7866bedfca6d42bbe4c20a4ad16f4ecb21d4c84": { + "0xa539bd29": 0 + }, + "0x181b971d54d29179cf25b3b69f79b028ea991fbf97cc8d4065d64bc53018932a": { + "0xfd228669": 0 + } + } + } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e71ae20..d6ff79a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -956,7 +956,7 @@ __metadata: languageName: node linkType: hard -"@aztec/pxe@npm:4.3.0-nightly.20260416, @aztec/pxe@npm:v4.3.0-nightly.20260416": +"@aztec/pxe@npm:4.3.0-nightly.20260416": version: 4.3.0-nightly.20260416 resolution: "@aztec/pxe@npm:4.3.0-nightly.20260416" dependencies: @@ -985,6 +985,35 @@ __metadata: languageName: node linkType: hard +"@aztec/pxe@patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch": + version: 4.3.0-nightly.20260416 + resolution: "@aztec/pxe@patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch::version=4.3.0-nightly.20260416&hash=784cb2" + dependencies: + "@aztec/bb-prover": "npm:4.3.0-nightly.20260416" + "@aztec/bb.js": "npm:4.3.0-nightly.20260416" + "@aztec/builder": "npm:4.3.0-nightly.20260416" + "@aztec/constants": "npm:4.3.0-nightly.20260416" + "@aztec/ethereum": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/key-store": "npm:4.3.0-nightly.20260416" + "@aztec/kv-store": "npm:4.3.0-nightly.20260416" + "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" + "@aztec/noir-types": "npm:4.3.0-nightly.20260416" + "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" + "@aztec/simulator": "npm:4.3.0-nightly.20260416" + "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + koa: "npm:^2.16.1" + koa-router: "npm:^13.1.1" + lodash.omit: "npm:^4.5.0" + sha3: "npm:^2.1.4" + tslib: "npm:^2.4.0" + viem: "npm:@aztec/viem@2.38.2" + bin: + pxe: dest/bin/index.js + checksum: 10c0/a7445b6727752bf3059b1de923bebe0d3cd501502c6dbb4cfcfe14885daaf97f37a2ab4d721145be7a689b8c6c9885981832579303d295d2b225d1f9225e85bd + languageName: node + linkType: hard + "@aztec/simulator@npm:4.3.0-nightly.20260416": version: 4.3.0-nightly.20260416 resolution: "@aztec/simulator@npm:4.3.0-nightly.20260416" From 73f5b52ca9a73e99e5b6e9532fe15d10913ff276 Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 17 Apr 2026 12:10:16 +0200 Subject: [PATCH 6/6] remove patch, updated --- ...pm-4.3.0-nightly.20260416-b01dc128ea.patch | 55 -- contracts/amm/Nargo.toml | 6 +- contracts/proof_of_password/Nargo.toml | 6 +- package.json | 29 +- src/config/networks/testnet.json | 6 +- src/profiling/index.ts | 39 +- yarn.lock | 590 +++++++++--------- 7 files changed, 336 insertions(+), 395 deletions(-) delete mode 100644 .yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch diff --git a/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch b/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch deleted file mode 100644 index db4ca2f..0000000 --- a/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/dest/storage/capsule_store/capsule_store.js b/dest/storage/capsule_store/capsule_store.js -index f455fc151d506562fbf20aa5b13b269a30c981b1..dccd521181ed58ccef3a23da2418f4f7648d671f 100644 ---- a/dest/storage/capsule_store/capsule_store.js -+++ b/dest/storage/capsule_store/capsule_store.js -@@ -108,11 +108,16 @@ export class CapsuleStore { - this.#setOnStage(jobId, dbSlotKey, Buffer.concat(capsule.map((value)=>value.toBuffer()))); - } - /** -- * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. -+ * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. Wraps the DB read in -+ * a transactionAsync so it enters the serial queue and never observes a stale IndexedDB transaction set by a -+ * concurrent transactionAsync caller. - * @param contractAddress - The contract address under which the data is scoped. - * @param slot - The slot in the database to read. - * @returns The stored data or `null` if no data is stored under the slot. - */ async getCapsule(contractAddress, slot, jobId, scope) { -+ return this.#store.transactionAsync(()=>this.#getCapsuleInternal(contractAddress, slot, jobId, scope)); -+ } -+ /** Same as getCapsule but without its own transaction — for use inside an existing transactionAsync. */ async #getCapsuleInternal(contractAddress, slot, jobId, scope) { - const dataBuffer = await this.#getFromStage(jobId, dbSlotToKey(contractAddress, slot, scope)); - if (!dataBuffer) { - this.logger.trace(`Data not found for contract ${contractAddress.toString()} and slot ${slot.toString()}`); -@@ -182,7 +187,7 @@ export class CapsuleStore { - // and not using a transaction here would heavily impact performance. - return this.#store.transactionAsync(async ()=>{ - // Load current length, defaulting to 0 if not found -- const lengthData = await this.getCapsule(contractAddress, baseSlot, jobId, scope); -+ const lengthData = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); - const currentLength = lengthData ? lengthData[0].toNumber() : 0; - // Store each capsule at consecutive slots after baseSlot + 1 + currentLength - for(let i = 0; i < content.length; i++){ -@@ -204,12 +209,12 @@ export class CapsuleStore { - // of jobs: different calls running concurrently on the same contract may cause trouble. - return this.#store.transactionAsync(async ()=>{ - // Load length, defaulting to 0 if not found -- const maybeLength = await this.getCapsule(contractAddress, baseSlot, jobId, scope); -+ const maybeLength = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); - const length = maybeLength ? maybeLength[0].toBigInt() : 0n; - const values = []; - // Read each capsule at consecutive slots after baseSlot - for(let i = 0; i < length; i++){ -- const currentValue = await this.getCapsule(contractAddress, arraySlot(baseSlot, i), jobId, scope); -+ const currentValue = await this.#getCapsuleInternal(contractAddress, arraySlot(baseSlot, i), jobId, scope); - if (currentValue == undefined) { - throw new Error(`Expected non-empty value at capsule array in base slot ${baseSlot} at index ${i} for contract ${contractAddress}`); - } -@@ -229,7 +234,7 @@ export class CapsuleStore { - // of jobs: different calls running concurrently on the same contract may cause trouble. - return this.#store.transactionAsync(async ()=>{ - // Load current length, defaulting to 0 if not found -- const maybeLength = await this.getCapsule(contractAddress, baseSlot, jobId, scope); -+ const maybeLength = await this.#getCapsuleInternal(contractAddress, baseSlot, jobId, scope); - const originalLength = maybeLength ? maybeLength[0].toNumber() : 0; - // Set the new length - this.setCapsule(contractAddress, baseSlot, [ diff --git a/contracts/amm/Nargo.toml b/contracts/amm/Nargo.toml index dbadd9e..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.3.0-nightly.20260416", directory = "noir-projects/aztec-nr/aztec" } -token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } -uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", 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 750b5fc..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.3.0-nightly.20260416", directory = "noir-projects/aztec-nr/aztec" } -token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.3.0-nightly.20260416", directory = "noir-projects/noir-contracts/contracts/app/token_contract" } +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.20260416", directory = "noir-projects/aztec-nr/compressed-string" } \ No newline at end of file +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 8741f6c..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.3.0-nightly.20260416", - "@aztec/aztec.js": "v4.3.0-nightly.20260416", - "@aztec/constants": "v4.3.0-nightly.20260416", - "@aztec/entrypoints": "v4.3.0-nightly.20260416", - "@aztec/foundation": "v4.3.0-nightly.20260416", - "@aztec/noir-contracts.js": "v4.3.0-nightly.20260416", - "@aztec/protocol-contracts": "v4.3.0-nightly.20260416", - "@aztec/pxe": "v4.3.0-nightly.20260416", - "@aztec/stdlib": "v4.3.0-nightly.20260416", - "@aztec/wallet-sdk": "v4.3.0-nightly.20260416", + "@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.16", - "@gregojuice/embedded-wallet": "^0.0.16", + "@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", @@ -51,11 +51,8 @@ "zod": "^3.23.8", "zone.js": "^0.16.1" }, - "resolutions": { - "@aztec/pxe": "patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch" - }, "devDependencies": { - "@aztec/wallets": "v4.3.0-nightly.20260416", + "@aztec/wallets": "v4.3.0-nightly.20260417", "@eslint/js": "^9.18.0", "@playwright/test": "1.49.0", "@types/buffer-json": "^2", diff --git a/src/config/networks/testnet.json b/src/config/networks/testnet.json index 987b6a8..eeb7394 100644 --- a/src/config/networks/testnet.json +++ b/src/config/networks/testnet.json @@ -17,8 +17,8 @@ }, "deployedAt": "2026-04-16T07:16:45.008Z", "subscriptionFPC": { - "address": "0x029f91316f06e4d46e1d77b831a394dbe01676480e9114403838b8df944ca043", - "secretKey": "0x2bd6014457e941f1ee656a5824cd6016289acfbd708a9019860284c898c9461c", + "address": "0x23fae3f4cdad2f6b36838eaf5020dd6817ff736ebcc2c7f7fa5a9e8f09b57f2f", + "secretKey": "0x061f4db024da8aa7a3eea724beee9dd6c78e4b51d2bca3bf95f8a5d1da94e594", "functions": { "0x0cc4ceb7cf7cc514ea8fbc5dc7866bedfca6d42bbe4c20a4ad16f4ecb21d4c84": { "0xa539bd29": 0 @@ -28,4 +28,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/profiling/index.ts b/src/profiling/index.ts index cb3ae3b..819ee28 100644 --- a/src/profiling/index.ts +++ b/src/profiling/index.ts @@ -106,6 +106,17 @@ function wrapAllMethods( 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 { @@ -254,23 +265,41 @@ class Profiler { if (pxe.simulator) wrapped.add(pxe.simulator); this._cleanups.push(installSimulatorInterceptorFromPXE(pxe, this)); - this.instrumentPxeInternals(pxe, wrapped); + this.instrumentInternals(pxe, wrapped, 3); } } - /** Walk PXE properties and wrap methods on internal services/stores. */ - private instrumentPxeInternals(pxe: any, alreadyWrapped: Set) { - for (const key of Object.getOwnPropertyNames(pxe)) { + /** + * 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 = pxe[key]; } catch { continue; } + 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); } } diff --git a/yarn.lock b/yarn.lock index d6ff79a..e672dd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,66 +645,66 @@ __metadata: languageName: node linkType: hard -"@aztec/accounts@npm:4.3.0-nightly.20260416, @aztec/accounts@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/accounts@npm:4.3.0-nightly.20260416" - dependencies: - "@aztec/aztec.js": "npm:4.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" +"@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/745a6d415d91828e337dd96a53f3c42f9ea1c88ccd6c9799e53013d8a1460189fda0e8788d49865245630dabcfdfd2f909a5398390df4983fe11763320591e3c + checksum: 10c0/bac0071c30830e05fcd3bbcc7bdf1294af3595b2c06fe40a773b7368d4240200653fcc9389370e760fa4759695487956b45143158ae01972467054b62370b88f languageName: node linkType: hard -"@aztec/aztec.js@npm:4.3.0-nightly.20260416, @aztec/aztec.js@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/aztec.js@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/a70d1431d631eaf878046505719ba5cee020f9db97a29e066bb572de35fca6d697527f00392a9122ca744615f7e00ae8c403a8c9bd18cfb2521b040b5876af8d + checksum: 10c0/ff6f61dad60f50fd295090f7ffd91a97a98bac0b4d6d0a0957ecfc579774ed297f36710588bf73cdfa86bb28cdc0d61bc69ac8666f9de71dfa55a1d3a66dce8d languageName: node linkType: hard -"@aztec/bb-prover@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/bb-prover@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" - "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" - "@aztec/noir-types": "npm:4.3.0-nightly.20260416" - "@aztec/simulator": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" - "@aztec/world-state": "npm:4.3.0-nightly.20260416" + "@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/c7e86a7b28953a9cd9cf2afc6a0fd012ff99db750a1bb59c26df960266096e337956a8ec469da44ee9fa4614aac2b92670b6a47507aeeaa66e7bb0eff9e296fc + checksum: 10c0/fc934eb838b89b6073e209983ccdeb42e74d357e73c49bec36e258e470e775a62f7d0210199d37002aebed2ca9078a5a864d124b4ba907b9bcc7560578c9a224 languageName: node linkType: hard -"@aztec/bb.js@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/bb.js@npm:4.3.0-nightly.20260416" +"@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/746906ed9688ac09981087f4fc4406c00819c5ff10087c9efbd12e153e431180be9275da5d8bca8c84348184ae19cab1b513a7e63ef7531652993c6bfe49f7fa + checksum: 10c0/f6ee4faa378a3772902c2d96aec0fe137531f0f1cc8b30b76972c12fc0d67e223f69e896632714c2447088dfc5c21d6840a6e0a06a8773c8b9a47fffaaa3e16a languageName: node linkType: hard -"@aztec/blob-lib@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/blob-lib@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@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/e83734b6b64d2d955ecce8bb1a28d205238fa2dbd234883f7e24093897bba924414f427c305c2fbd19cd05629d1377fe72bfa8a9f347a7685a56542ad1711db2 + checksum: 10c0/aae5bcb04ee2d631b23bcb4bbdee6eb1ca2d280e7cec62811656d0e0ce0cd0286afd48dadff5fb06f90cb44d0834b516ac07c522c463945e229df86a9e17c756 languageName: node linkType: hard -"@aztec/builder@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/builder@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" + "@aztec/stdlib": "npm:4.3.0-nightly.20260417" commander: "npm:^12.1.0" - checksum: 10c0/9f26fd350660171c98773f2fb62967a96be4f7cea1816f83123b39c6a19547fb562e5e33ea32c72fd066342b67e18ace6139e7b12105c7d36ad9ab7a9f7b0568 + checksum: 10c0/8f1adbb402a809db671027bafc54e3a0a60bae086f6a86e43ac7dbf5838e7075f18b9ffbed81798bf524ef7dc25ab1b652564ba8c59aa26e7e6293cf16cbe8e7 languageName: node linkType: hard -"@aztec/constants@npm:4.3.0-nightly.20260416, @aztec/constants@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/constants@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/c0121fe6e9c79e35641a4f80ea8a01a8082d4e18550aaa8e8a1f7d95430241c72fce30e58dbaaf48da598b3087dd7ae92de4b95bfd4233f5bcc6d8cdc4119149 + checksum: 10c0/3589bd9a42ecd6da3a1b542f547227a3fdc9f6bc9654d3f8faecd021a14481d6be46831890dadcc6a8fe2fa1bdafa88bf90aec0844033e8899a51ca1dd7cef48 languageName: node linkType: hard -"@aztec/entrypoints@npm:4.3.0-nightly.20260416, @aztec/entrypoints@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/entrypoints@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/eb0d6aa6590af77c0a985b587368ba7d8ddb47d47c7966d1a8361150e6a80204631295486271a0bd5cd48d49487b0a7d1c45e2e2dc255a6cf10c904b2a09f30c + checksum: 10c0/1b038e4d939edbb19c75902352f73016a25e915cd19929f01262a0e54e64b2da19041f553705d2c79349b5c6cbdf26e8224f8ad6c7e7ec21ff1b22284aa7b9a1 languageName: node linkType: hard -"@aztec/ethereum@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/ethereum@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" + "@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/bc232e54e7a23be3cc68e841518a205d26a78a5c9788bb8eb23bdb79877b5ab0d5b31966062349dcd4d966fb1b70d85a1c6dea58fcd74bb42b1c0e7efe553177 + checksum: 10c0/0cf832cfb468186bb0a78384f866f60aca10a86c3d7de3509c3faa91e347fbc8fde8c051aed57969d9274b4ed31c370225f3b85421ee0f254ac72798378db194 languageName: node linkType: hard -"@aztec/foundation@npm:4.3.0-nightly.20260416, @aztec/foundation@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/foundation@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" + "@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/ced8edaa8e387cf0076cc759e1e82d02d3719ccec01c74448e9149996d0ec5aec906032f0db7e1fc1cc0c5c1134c0107ea356b8fa81d1e1730568f1bb11d99e4 + checksum: 10c0/f54e7477ab56b4edca07d19a444f82511995399010895509bba124542a6399e013690c68ed26c62582241e2ea3686656b0ee51b38c5e6ee8ef579c83516491fa languageName: node linkType: hard -"@aztec/key-store@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/key-store@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/34e0592dcf6f5077eb26a209c8219aa88199e8dbb75432f76e592fed6ff15d47fe21f794848f8f891a4217b4026f2efdc1594b1a224720b2e9b2b174cdc2b9d8 + checksum: 10c0/f9757cd36ba38ec4882e1ad924b1ebec21bafe29932bbd6c3946ebbe41b94a15bffa698cbfe3b34445f225138c280e92728e4714fc3b6ff2086d4b23d7519418 languageName: node linkType: hard -"@aztec/kv-store@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/kv-store@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/native": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/2131efeffad61cdb3913bf46e59f48db42ccf17dbd6a99d18d55cc34d7812b7fafed9d765965904b5487446236e01e3f6d63a2dd5694847014815f5c7ae784a1 + checksum: 10c0/72cbfd4be93452a7d6b6fb946f8803caef11d7867875f6a9a0ad075e7cbec34ec24493352ce52e4d1ef8e09d8827087eb65f605b7666e98819ed6fe12e474d22 languageName: node linkType: hard -"@aztec/l1-artifacts@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/l1-artifacts@npm:4.3.0-nightly.20260416" +"@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/b418d92180c4511b5f68db36060c64c8b5f58c72b8b728d7a4a906b1de13d2bdd5120cea6d5a848de12f8454571773f01206181782f0a88e82b395386760d2c2 + checksum: 10c0/04f1cb4e2af273d5176562c2a86a336f8bfa6d2bef1591d11a2b39bdf582b4b45142e791cd5a8fe3f1153e85140729f830b30aecd16d85f777d96a198482b091 languageName: node linkType: hard -"@aztec/merkle-tree@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/merkle-tree@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/1c40422b2b6d8da7b133f94597438df0dcf2a50912be4e0e7fa4f3a895317f75f902b103d63c34636c732c471d8d0ef1528eaa2770884f4315056e28b266c4a1 + checksum: 10c0/b7670bcb4ac9aab1dcad531aeb0cb19023d61a5082a6b5e1c187d10500e97c5acd2d02e59e26df3a552ed54c6f7efa83f8221ffdbdaf19919e3ade8bbef6a6b2 languageName: node linkType: hard -"@aztec/native@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/native@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@aztec/bb.js": "npm:4.3.0-nightly.20260417" + "@aztec/foundation": "npm:4.3.0-nightly.20260417" msgpackr: "npm:^1.11.2" - checksum: 10c0/7e4f93c1da13487e3577af5857b4d54cfe0293bdb867a4ee351c8ed90afc77aa9a7fb1643936c2c869f64d504a371b3a3974d6dffc5dae587ca93da6fdfda633 + checksum: 10c0/97e5df87a21eb4670cbf7093d30f10af9db8336b2ab749bc32e5fb5a8460fe43dff6bf11745d2715e83cc1e5b632dcc399e33d7edac2710aa59ed07ef0cfe94b languageName: node linkType: hard -"@aztec/noir-acvm_js@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-acvm_js@npm:4.3.0-nightly.20260416" - checksum: 10c0/8013baf3075d0dfe3eed001eafedf0c01b1bb4096fd989438f92d71023e9d91ca8a4d69df53c7157d657bcd2a22e7702d80a3f96966465fda9f877bff191569b +"@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.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-contracts.js@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" + "@aztec/aztec.js": "npm:4.3.0-nightly.20260417" tslib: "npm:^2.4.0" - checksum: 10c0/6e3b864cccb1b551887d7069d47cac4ad41e7904b8a5ac68627bdae7648942111ca271f8f7dd6de5803f4bc59241d511760bde2ea049be80ad5751f726d5df3e + checksum: 10c0/49b0b41b8f83f3a73fd6fcdb0bf62efb16b3466138e620a97e24b3a1bceeeb6d939c1167a51a7c40c3208396e7f3724bcf88870647988b3a3eed7e8b0cb9053f languageName: node linkType: hard -"@aztec/noir-noir_codegen@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-noir_codegen@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" + "@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/97f6c7ee215e44be9d37f1d233a4337c3025acab49de6471e5b4d907ddc61b33b131eed82945f58330544872f9e6883f7565571ce7ef76f5db30dfe20b4bee82 + checksum: 10c0/43622589b73c51a1ab4abb1dcd7026a9bb686e1fdb455083d5e99f34536270b73d3b61314473bc7f3d69d309c6bb10bd18bd8bde67a79acde71512a10d61cc82 languageName: node linkType: hard -"@aztec/noir-noirc_abi@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-noirc_abi@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - checksum: 10c0/a9712c9d950c7b31eaaa302984124ed6a6dd8c805480decc667a4379f53091076975cd8d4aa69cd1c9a3feb9aed486e050ddfb3b090bb7aaeb094313b90c217c + "@aztec/noir-types": "npm:4.3.0-nightly.20260417" + checksum: 10c0/9575fcbc1855888e360a23cc0538ca35957d49adde874085132fc811783694ef7e4aed92b61d7de80a3355e8568f095b2268ba27920fa2662bd57028263df4a4 languageName: node linkType: hard -"@aztec/noir-protocol-circuits-types@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-protocol-circuits-types@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260416" - "@aztec/noir-noir_codegen": "npm:4.3.0-nightly.20260416" - "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" - "@aztec/noir-types": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/1a38d8f3a7d884a2575a67565945cfee32b30370e69182fd5fdcd254ae67e856a34536bab187c1acd8a14c5be2f4aaf75ec1385c3a07d31dd29ca8ea2911f52e + checksum: 10c0/cd02171472923ca0135424437cf7c284467e2e862cf956ec05ab1aa75fcfa7c4fe1c9dae7b33e3dd99d150dc18c92f24b5ca9fb717a272505db6f1e36e9912b4 languageName: node linkType: hard -"@aztec/noir-types@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/noir-types@npm:4.3.0-nightly.20260416" - checksum: 10c0/15968ec7e077fc9e011a3bb51d83e44cccd46e1b8db6e3e6cfcf4763cada5a3d8ea99f84eb1d7eaae3e8252fa83df0f92c39d95044f0e10db4660bae28d3b385 +"@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.3.0-nightly.20260416, @aztec/protocol-contracts@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/protocol-contracts@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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/42fa4fd67f87f07025fdec98455999a7f67464339736b37f8ebc582833ba7c8d4a1d1278bc6e2430c0f1db5893686fa9064f076994e7fb5122a92bf5d26ca382 - languageName: node - linkType: hard - -"@aztec/pxe@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/pxe@npm:4.3.0-nightly.20260416" - dependencies: - "@aztec/bb-prover": "npm:4.3.0-nightly.20260416" - "@aztec/bb.js": "npm:4.3.0-nightly.20260416" - "@aztec/builder": "npm:4.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/key-store": "npm:4.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" - "@aztec/noir-types": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/simulator": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + 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,74 +981,45 @@ __metadata: viem: "npm:@aztec/viem@2.38.2" bin: pxe: dest/bin/index.js - checksum: 10c0/9616043cd053a6db25e2026100cf2fb06d01cd3f18076b9d6da74f42a3432504ace78efcf0200ed32a69834c24a459af07f8544f3e1e3cd507e96f74e6adc8e0 - languageName: node - linkType: hard - -"@aztec/pxe@patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/pxe@patch:@aztec/pxe@npm%3A4.3.0-nightly.20260416#~/.yarn/patches/@aztec-pxe-npm-4.3.0-nightly.20260416-b01dc128ea.patch::version=4.3.0-nightly.20260416&hash=784cb2" - dependencies: - "@aztec/bb-prover": "npm:4.3.0-nightly.20260416" - "@aztec/bb.js": "npm:4.3.0-nightly.20260416" - "@aztec/builder": "npm:4.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/key-store": "npm:4.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" - "@aztec/noir-types": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/simulator": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - koa: "npm:^2.16.1" - koa-router: "npm:^13.1.1" - lodash.omit: "npm:^4.5.0" - sha3: "npm:^2.1.4" - tslib: "npm:^2.4.0" - viem: "npm:@aztec/viem@2.38.2" - bin: - pxe: dest/bin/index.js - checksum: 10c0/a7445b6727752bf3059b1de923bebe0d3cd501502c6dbb4cfcfe14885daaf97f37a2ab4d721145be7a689b8c6c9885981832579303d295d2b225d1f9225e85bd + checksum: 10c0/383c73ed82b64c3dad58cddaf4b4d560e85a4edc8ce7125e9196d6bf3df4db92e990a2d0243724a6810d44e645f783bc444c9a6ee04770b1367381a62507887c languageName: node linkType: hard -"@aztec/simulator@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/simulator@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/native": "npm:4.3.0-nightly.20260416" - "@aztec/noir-acvm_js": "npm:4.3.0-nightly.20260416" - "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" - "@aztec/noir-protocol-circuits-types": "npm:4.3.0-nightly.20260416" - "@aztec/noir-types": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" - "@aztec/world-state": "npm:4.3.0-nightly.20260416" + "@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/abf62dba2e38c5c1a554b30d3cf5c1b3e9a9aef4702fc25e3726d273f8e0fad9478585074f31e23f6e96328bd08724d2332c23dc954b11acfd5577263eab76fc + checksum: 10c0/e47881ec005099d0bbeeec8a5c7f1ce6b11d88d929b19fbcac816e8710b48cf4afab2d8b486c8c461373a8b8800708d68ddf2ad83846eea085438dd407cc288b languageName: node linkType: hard -"@aztec/stdlib@npm:4.3.0-nightly.20260416, @aztec/stdlib@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/stdlib@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/blob-lib": "npm:4.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/ethereum": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/l1-artifacts": "npm:4.3.0-nightly.20260416" - "@aztec/noir-noirc_abi": "npm:4.3.0-nightly.20260416" - "@aztec/validator-ha-signer": "npm:4.3.0-nightly.20260416" + "@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" @@ -1061,16 +1032,16 @@ __metadata: tslib: "npm:^2.4.0" viem: "npm:@aztec/viem@2.38.2" zod: "npm:^3.23.8" - checksum: 10c0/b3deccd1fa6f0028c15e1c895d88487740e6497b6f1332742f656b21db06f904524cd58c13b93f48ebe194673fd980b6bb62e91e2736072ec090d4ed23753264 + checksum: 10c0/b295d4b7e129d1b8a5e4229a287123b6c83cdf24eb5285ec5b72d22bb31669edf2cecbd2b495bede0c5574239c4dd225dac7babf3e73d00bed9acce7428d8bca languageName: node linkType: hard -"@aztec/telemetry-client@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/telemetry-client@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" + "@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" @@ -1087,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/717220c7cdb69b7f0d570c8de75035520b4f978a2eab8de4e687bef6f7b87df344367c520526275f073529e2eee8f3ebf233825ea5fc6d976d2666c464767c58 + checksum: 10c0/3bc43c38c89f082c93e219aa83391cf3a4230fd8d2d0c92baf3d75e0bb84b7063e045689cd493e326f10cf30a7bc567048d181245d3a89d9cc2fe80b218e7f16 languageName: node linkType: hard -"@aztec/validator-ha-signer@npm:4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/validator-ha-signer@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" + "@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/403f876be2fb0783bcb2ab043e46e05bf4f9f273335d5f07070b8026833c2d9ab2726cac9f8246286d3905d24d1fc229c61cb8a20acae744e7378119ea8fba98 + checksum: 10c0/78a788cf21b052a933cf145d722edf74bc769b713b676c0dfc94481e176c7e9f398a0fccc6883255b311172be651c1cb892826d42c37863663eb5bd17ad7dade languageName: node linkType: hard -"@aztec/wallet-sdk@npm:4.3.0-nightly.20260416, @aztec/wallet-sdk@npm:v4.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/wallet-sdk@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/constants": "npm:4.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/pxe": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - checksum: 10c0/3ec66c35a202b9f5bd6f64330273a3408186f6d31544db7e33db88fd3309a45e3bdedebb8050c4d57e813ab4761a642682283b98e17e783dbb8689fdeb480c5c + "@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.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/wallets@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/aztec.js": "npm:4.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:4.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/pxe": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - "@aztec/wallet-sdk": "npm:4.3.0-nightly.20260416" - checksum: 10c0/dd35237d936944bcdbdda9d7be3ddd6fc4c360fa790c7b445ca53f19eb2df9e34d608a66156e9bb2372176b4218cc0a0cbfc666179df8536e9a5d08368c70c4f + "@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.3.0-nightly.20260416": - version: 4.3.0-nightly.20260416 - resolution: "@aztec/world-state@npm:4.3.0-nightly.20260416" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:4.3.0-nightly.20260416" - "@aztec/kv-store": "npm:4.3.0-nightly.20260416" - "@aztec/merkle-tree": "npm:4.3.0-nightly.20260416" - "@aztec/native": "npm:4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:4.3.0-nightly.20260416" - "@aztec/telemetry-client": "npm:4.3.0-nightly.20260416" + "@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/00fda56b2d05f7523047b143fe3870a74a0dc90b26eff05a8893ecbf02447f81a5e45441ff0e4e35732b8e2d782f8b8bf96beb460e669a3320b730d08b5f59a3 + checksum: 10c0/67b469ccfea54d8531e85a448db7e0d71a475535aa9e468993fc932cc0b02b99be11c12c32926186934c1f9567101d7661f2a893c7d0f6b3dcc3b0bfdd36b292 languageName: node linkType: hard @@ -1802,30 +1773,29 @@ __metadata: languageName: node linkType: hard -"@gregojuice/contracts@npm:0.0.16, @gregojuice/contracts@npm:^0.0.16": - version: 0.0.16 - resolution: "@gregojuice/contracts@npm:0.0.16" +"@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.3.0-nightly.20260416" - "@aztec/foundation": "npm:v4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" - "@aztec/wallets": "npm:v4.3.0-nightly.20260416" - checksum: 10c0/055ddbe4b9c3c946a5cce411dbcb8c20178ec2157d5608eb644a95f92f6288fdf41ead844b746b64dd5ac9128c5ba98dbb01708e74d6a07d96dcb8878e2893c6 + "@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.16": - version: 0.0.16 - resolution: "@gregojuice/embedded-wallet@npm:0.0.16" +"@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.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:v4.3.0-nightly.20260416" - "@aztec/foundation": "npm:v4.3.0-nightly.20260416" - "@aztec/pxe": "npm:v4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" - "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260416" - "@aztec/wallets": "npm:v4.3.0-nightly.20260416" - "@gregojuice/contracts": "npm:0.0.16" + "@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" @@ -1840,7 +1810,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/e6f46f561021a1cd24daf4a144a928105e2e95028f9859daa574de84a425213356cdbbdde9dda0068b56186218473de65e7010a4dd34652d9fe556c4a632a2c9 + checksum: 10c0/c3c3c9a65179bb01135b21500c392fdf852d429ebd38adbe0df9d1876a5fac8c1f0df5a2ca549c403e442b85532ad262a82f726830e7a13a71eb108c2f1df030 languageName: node linkType: hard @@ -6115,22 +6085,22 @@ __metadata: version: 0.0.0-use.local resolution: "gregoswap@workspace:." dependencies: - "@aztec/accounts": "npm:v4.3.0-nightly.20260416" - "@aztec/aztec.js": "npm:v4.3.0-nightly.20260416" - "@aztec/constants": "npm:v4.3.0-nightly.20260416" - "@aztec/entrypoints": "npm:v4.3.0-nightly.20260416" - "@aztec/foundation": "npm:v4.3.0-nightly.20260416" - "@aztec/noir-contracts.js": "npm:v4.3.0-nightly.20260416" - "@aztec/protocol-contracts": "npm:v4.3.0-nightly.20260416" - "@aztec/pxe": "npm:v4.3.0-nightly.20260416" - "@aztec/stdlib": "npm:v4.3.0-nightly.20260416" - "@aztec/wallet-sdk": "npm:v4.3.0-nightly.20260416" - "@aztec/wallets": "npm:v4.3.0-nightly.20260416" + "@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.16" - "@gregojuice/embedded-wallet": "npm:^0.0.16" + "@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"