Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ on:

env:
PASSWORD: ${{ secrets.PASSWORD }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

permissions:
contents: read
Expand Down Expand Up @@ -67,13 +69,17 @@ jobs:
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy to Vercel
- name: Build and deploy to Vercel
id: deploy
env:
VITE_WEB_WALLET_URL: ${{ vars.VITE_WEB_WALLET_URL }}
run: |
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == "refs/heads/main" ]; then
DEPLOY_URL=$(vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} --archive=tgz --yes)
vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
DEPLOY_URL=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} --archive=tgz --yes)
else
DEPLOY_URL=$(vercel deploy --token=${{ secrets.VERCEL_TOKEN }} --archive=tgz --yes)
vercel build --token=${{ secrets.VERCEL_TOKEN }}
DEPLOY_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} --archive=tgz --yes)
fi
echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ VERSION=4.0.0-nightly.20260205 bash -i <(curl -sL https://install.aztec.network/

### 3. Set Aztec Version

The project uses Aztec version `v4.0.0-devnet.1-patch.0`. Set it using:
The project uses Aztec version `v4.0.0-devnet.2-patch.3`. Set it using:

```bash
aztec-up install 4.0.0-nightly.20260205
Expand Down
6 changes: 3 additions & 3 deletions contracts/proof_of_password/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type = "contract"
authors = [""]

[dependencies]
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.0.0-devnet.2-patch.1", directory = "noir-projects/aztec-nr/aztec" }
token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.0.0-devnet.2-patch.1", directory = "noir-projects/noir-contracts/contracts/app/token_contract" }
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.0.0-devnet.2-patch.3", directory = "noir-projects/aztec-nr/aztec" }
token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.0.0-devnet.2-patch.3", 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.0.0-devnet.2-patch.1", directory = "noir-projects/aztec-nr/compressed-string" }
compressed_string = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v4.0.0-devnet.2-patch.3", directory = "noir-projects/aztec-nr/compressed-string" }
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"serve": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"copy:dependencies": "cd contracts && nargo check && WORKDIR=$(pwd) && cd $HOME/nargo/github.com/AztecProtocol/aztec-packages/v4.0.0-devnet.2-patch.1/noir-projects/noir-contracts && aztec compile --package token_contract && mkdir -p $WORKDIR/target && cp $HOME/nargo/github.com/AztecProtocol/aztec-packages/v4.0.0-devnet.2-patch.1/noir-projects/noir-contracts/target/token_contract-Token.json $WORKDIR/target/token_contract-Token.json",
"copy:dependencies": "cd contracts && nargo check && WORKDIR=$(pwd) && cd $HOME/nargo/github.com/AztecProtocol/aztec-packages/v4.0.0-devnet.2-patch.3/noir-projects/noir-contracts && aztec compile --package token_contract && mkdir -p $WORKDIR/target && cp $HOME/nargo/github.com/AztecProtocol/aztec-packages/v4.0.0-devnet.2-patch.3/noir-projects/noir-contracts/target/token_contract-Token.json $WORKDIR/target/token_contract-Token.json",
"compile:contracts": "cd contracts && aztec compile --package proof_of_password && aztec codegen ./target/proof_of_password-ProofOfPassword.json",
"test": "cd contracts && aztec test",
"preview": "vite preview",
Expand All @@ -23,16 +23,16 @@
"local-aztec:status": "node scripts/toggle-local-aztec.js status"
},
"dependencies": {
"@aztec/accounts": "v4.0.0-devnet.2-patch.1",
"@aztec/aztec.js": "v4.0.0-devnet.2-patch.1",
"@aztec/constants": "v4.0.0-devnet.2-patch.1",
"@aztec/entrypoints": "v4.0.0-devnet.2-patch.1",
"@aztec/foundation": "v4.0.0-devnet.2-patch.1",
"@aztec/noir-contracts.js": "v4.0.0-devnet.2-patch.1",
"@aztec/protocol-contracts": "v4.0.0-devnet.2-patch.1",
"@aztec/pxe": "v4.0.0-devnet.2-patch.1",
"@aztec/stdlib": "v4.0.0-devnet.2-patch.1",
"@aztec/wallet-sdk": "v4.0.0-devnet.2-patch.1",
"@aztec/accounts": "v4.0.0-devnet.2-patch.3",
"@aztec/aztec.js": "v4.0.0-devnet.2-patch.3",
"@aztec/constants": "v4.0.0-devnet.2-patch.3",
"@aztec/entrypoints": "v4.0.0-devnet.2-patch.3",
"@aztec/foundation": "v4.0.0-devnet.2-patch.3",
"@aztec/noir-contracts.js": "v4.0.0-devnet.2-patch.3",
"@aztec/protocol-contracts": "v4.0.0-devnet.2-patch.3",
"@aztec/pxe": "v4.0.0-devnet.2-patch.3",
"@aztec/stdlib": "v4.0.0-devnet.2-patch.3",
"@aztec/wallet-sdk": "v4.0.0-devnet.2-patch.3",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.3.1",
Expand All @@ -45,7 +45,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@aztec/wallets": "v4.0.0-devnet.2-patch.1",
"@aztec/wallets": "v4.0.0-devnet.2-patch.3",
"@eslint/js": "^9.18.0",
"@playwright/test": "1.49.0",
"@types/buffer-json": "^2",
Expand Down
123 changes: 103 additions & 20 deletions src/components/TxNotificationCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
import { txProgress, type TxProgressEvent, type PhaseTiming } from '../tx-progress';

// ─── Live phase support ───────────────────────────────────────────────────────

interface LivePhaseTiming extends PhaseTiming {
isLive?: boolean;
}

const ACTIVE_PHASE_COLORS: Record<string, string> = {
simulating: '#ce93d8',
proving: '#f48fb1',
sending: '#2196f3',
mining: '#4caf50',
};

const shimmer = keyframes`
0% { background-position: -400px 0; }
100% { background-position: 400px 0; }
`;

// ─── Helpers ─────────────────────────────────────────────────────────────────

const formatDuration = (ms: number): string => {
Expand Down Expand Up @@ -60,22 +78,29 @@ const pulse = keyframes`

// ─── PhaseTimeline (inline, simplified from demo-wallet) ─────────────────────

function PhaseTimelineBar({ phases }: { phases: PhaseTiming[] }) {
const totalDuration = useMemo(() => phases.reduce((sum, p) => sum + p.duration, 0), [phases]);
function PhaseTimelineBar({ phases }: { phases: LivePhaseTiming[] }) {
const completedPhases = useMemo(() => phases.filter(p => !p.isLive), [phases]);
const livePhase = useMemo(() => phases.find(p => p.isLive), [phases]);

const completedDuration = useMemo(() => completedPhases.reduce((sum, p) => sum + p.duration, 0), [completedPhases]);
const liveDuration = livePhase?.duration ?? 0;
const totalDuration = completedDuration + liveDuration;

const miningDuration = useMemo(
() => phases.filter(p => p.name === 'Mining').reduce((sum, p) => sum + p.duration, 0),
[phases],
() => completedPhases.filter(p => p.name === 'Mining').reduce((sum, p) => sum + p.duration, 0),
[completedPhases],
);

if (phases.length === 0 || totalDuration === 0) return null;

const preparingDuration = totalDuration - miningDuration;
const hasMining = miningDuration > 0;
const hasLive = !!livePhase;

return (
<Box sx={{ width: '100%', mt: 1.5 }}>
{/* Summary chips */}
<Box sx={{ display: 'flex', gap: 0.5, mb: 0.5, flexWrap: 'wrap' }}>
<Box sx={{ display: 'flex', gap: 0.5, mb: 0.5, flexWrap: 'wrap', alignItems: 'center' }}>
{hasMining ? (
<>
<Chip
Expand All @@ -96,7 +121,7 @@ function PhaseTimelineBar({ phases }: { phases: PhaseTiming[] }) {
</>
) : (
<Chip
label={`Total: ${formatDuration(totalDuration)}`}
label={hasLive ? `Elapsed: ${formatDuration(totalDuration)}` : `Total: ${formatDuration(totalDuration)}`}
size="small"
sx={{ height: 18, fontSize: '0.6rem', fontWeight: 600 }}
/>
Expand All @@ -114,7 +139,8 @@ function PhaseTimelineBar({ phases }: { phases: PhaseTiming[] }) {
bgcolor: 'action.hover',
}}
>
{phases.map((phase, index) => {
{/* Completed segments (proportional width based on total) */}
{completedPhases.map((phase, index) => {
const percentage = (phase.duration / totalDuration) * 100;
return (
<Tooltip
Expand Down Expand Up @@ -165,7 +191,7 @@ function PhaseTimelineBar({ phases }: { phases: PhaseTiming[] }) {
minWidth: percentage > 0 ? 2 : 0,
height: '100%',
bgcolor: phase.color,
borderRight: index < phases.length - 1 ? '1px solid rgba(255,255,255,0.3)' : undefined,
borderRight: (index < completedPhases.length - 1 || hasLive) ? '1px solid rgba(255,255,255,0.3)' : undefined,
transition: 'filter 0.2s ease',
cursor: 'pointer',
'&:hover': { filter: 'brightness(1.2)' },
Expand All @@ -174,15 +200,43 @@ function PhaseTimelineBar({ phases }: { phases: PhaseTiming[] }) {
</Tooltip>
);
})}

{/* Live (shimmer) segment — flex: 1 to fill remaining space */}
{livePhase && (
<Tooltip
title={
<Box sx={{ p: 0.5 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
{livePhase.name}
</Typography>
<Typography variant="body2">{formatDurationLong(livePhase.duration)} (in progress)</Typography>
</Box>
}
arrow
placement="top"
>
<Box
sx={{
flex: 1,
minWidth: 40,
height: '100%',
background: `linear-gradient(90deg, ${livePhase.color}88 0%, ${livePhase.color} 50%, ${livePhase.color}88 100%)`,
backgroundSize: '400px 100%',
animation: `${shimmer} 1.5s infinite linear`,
cursor: 'pointer',
}}
/>
</Tooltip>
)}
</Box>

{/* Legend */}
<Box sx={{ display: 'flex', gap: 1, mt: 0.5, flexWrap: 'wrap' }}>
{phases.map(phase => (
<Box key={phase.name} sx={{ display: 'flex', alignItems: 'center', gap: 0.3 }}>
<Box sx={{ width: 6, height: 6, borderRadius: '50%', bgcolor: phase.color }} />
<Box sx={{ width: 6, height: 6, borderRadius: '50%', bgcolor: phase.color, ...(phase.isLive && { animation: `${pulse} 1.2s ease-in-out infinite` }) }} />
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.6rem' }}>
{phase.name}
{phase.name}{phase.isLive ? ' ●' : ''}
</Typography>
</Box>
))}
Expand All @@ -201,14 +255,16 @@ interface TxToastProps {
function TxToast({ event, onDismiss }: TxToastProps) {
const isActive = event.phase !== 'complete' && event.phase !== 'error';

// For completed events, compute total from recorded phase timings (stable across refreshes)
// For completed events, compute total from recorded phases (stable across refreshes)
const computeFinalElapsed = () => {
const t = event.phaseTimings;
const fromTimings = (t.simulation ?? 0) + (t.proving ?? 0) + (t.sending ?? 0) + (t.mining ?? 0);
return fromTimings > 0 ? fromTimings : Date.now() - event.startTime;
const fromPhases = event.phases.reduce((sum, p) => sum + p.duration, 0);
return fromPhases > 0 ? fromPhases : Date.now() - event.startTime;
};

// Total wall-clock elapsed since tx start (for header display)
const [elapsed, setElapsed] = useState(() => isActive ? Date.now() - event.startTime : computeFinalElapsed());
// Live elapsed within the *current* phase (resets when phase changes)
const [phaseElapsed, setPhaseElapsed] = useState(() => isActive ? Date.now() - event.phaseStartTime : 0);
const [expanded, setExpanded] = useState(true);
const frozen = useRef(!isActive);

Expand All @@ -218,17 +274,44 @@ function TxToast({ event, onDismiss }: TxToastProps) {
if (!frozen.current) {
frozen.current = true;
setElapsed(computeFinalElapsed());
setPhaseElapsed(0);
}
return;
}
frozen.current = false;
const interval = setInterval(() => setElapsed(Date.now() - event.startTime), 200);
const interval = setInterval(() => {
setElapsed(Date.now() - event.startTime);
setPhaseElapsed(Date.now() - event.phaseStartTime);
}, 200);
return () => clearInterval(interval);
}, [isActive, event.startTime]);
}, [isActive, event.startTime, event.phaseStartTime]);

// Re-initialize elapsed when txId changes (new transaction)
const prevTxIdRef = useRef(event.txId);
useEffect(() => {
if (event.txId !== prevTxIdRef.current) {
prevTxIdRef.current = event.txId;
setElapsed(isActive ? Date.now() - event.startTime : computeFinalElapsed());
setPhaseElapsed(isActive ? Date.now() - event.phaseStartTime : 0);
frozen.current = !isActive;
}
}, [event.txId]);

const isComplete = event.phase === 'complete';
const isError = event.phase === 'error';

// Build display phases: completed phases + live shimmer phase when active
const displayPhases: LivePhaseTiming[] = useMemo(() => {
if (!isActive) return event.phases;
if (phaseElapsed <= 0 && event.phases.length === 0) return [];
const liveColor = ACTIVE_PHASE_COLORS[event.phase] ?? '#90caf9';
const liveName = PHASE_LABELS[event.phase] ?? event.phase;
return [
...event.phases,
{ name: liveName, duration: phaseElapsed > 0 ? phaseElapsed : 100, color: liveColor, isLive: true },
];
}, [isActive, event.phases, event.phase, phaseElapsed]);

return (
<Paper
elevation={8}
Expand Down Expand Up @@ -304,7 +387,7 @@ function TxToast({ event, onDismiss }: TxToastProps) {
</Typography>

{/* Expand/collapse */}
{isComplete && event.phases.length > 0 && (
{displayPhases.length > 0 && (
<IconButton size="small" onClick={() => setExpanded(prev => !prev)} sx={{ p: 0.25 }}>
{expanded ? <ExpandLessIcon sx={{ fontSize: 16 }} /> : <ExpandMoreIcon sx={{ fontSize: 16 }} />}
</IconButton>
Expand All @@ -316,10 +399,10 @@ function TxToast({ event, onDismiss }: TxToastProps) {
</IconButton>
</Box>

{/* Phase timeline breakdown (shown when complete) */}
<Collapse in={isComplete && expanded && event.phases.length > 0}>
{/* Phase timeline breakdown (shown during execution and when complete) */}
<Collapse in={expanded && displayPhases.length > 0}>
<Box sx={{ px: 1.5, pb: 1.5 }}>
<PhaseTimelineBar phases={event.phases} />
<PhaseTimelineBar phases={displayPhases} />
</Box>
</Collapse>

Expand Down
Loading