Parallel analysis lane
In the repo /home/akitav2/projects/4626, verify two frontend audit findings:
H-2 (DeployVault): In frontend/src/pages/deploy/DeployVault.tsx, find the submit() function (~line 4544) and the primary "1-Click Deploy" button (~line 8314). Report: (a) the exact guard condition in submit() (which busy flags it checks), (b) the deploy button's disabled prop expression, (c) where dryRunBusy state is declared and set, (d) line numbers for all three. Also check whether there are existing RTL/component tests for DeployVault (search for DeployVault test files).
H-3 (Swap auto-quote): In frontend/src/pages/Swap.tsx (~lines 903-913) find the debounced auto-quote effect and report its exact skip conditions. In frontend/src/hooks/useSwapExecution.ts (~lines 1737-1740) find quoteRunRef usage, handleReviewTrade, handleQuote, and the busy state values ('review', 'quote', 'executeSwap'). Report: (a) what conditions currently pause the auto-quote, (b) how quoteRunRef epoch-bumping causes review to bail (quote the runId check), (c) whether handleReviewTrade guards on busy, (d) what txState values exist. Also find existing tests for useSwapExecution (search for useSwapExecution.test or swap hook tests) and report how they set up the hook.
Be precise with file:line citations for everything.
Analysis
Searching DeployVault and Swap code paths and related tests for precise line citations.
[REDACTED]
Tools invoked: UpdateCurrentStep, Grep, Grep, Grep, Read, Grep, Glob
Analysis
[REDACTED]
Tools invoked: Grep, Read, Grep, Read, Read, Read, Read, Read
Analysis
[REDACTED]
Tools invoked: Read, Read, Read, Grep, Grep, Glob
Analysis
[REDACTED]
Tools invoked: Grep, Read, Grep, Grep
Analysis
[REDACTED]
Tools invoked: Read, Grep, Read, Read, Grep
Analysis
[REDACTED]
Tools invoked: Glob, Grep, Read
Analysis
[REDACTED]
Tools invoked: Read, Grep, Grep
Analysis
[REDACTED]
Tools invoked: UpdateCurrentStep
Analysis
H-2 — DeployVault (DeployVault.tsx)
(a) submit() guard condition — line 4558
const submit = async (opts?: { planOnly?: boolean; validateDepositBalance?: boolean }): Promise<DeployPlanExport | null> => {
const planOnly = opts?.planOnly === true
const validateDepositBalance = !planOnly || opts?.validateDepositBalance === true
if (busy || exportBusy) return null
Checked: busy, exportBusy only.
Not checked: dryRunBusy.
(planOnly dry-run calls submit({ planOnly: true }) at 7268 and does not set busy — see 4579–4580.)
(b) Primary “1-Click Deploy” button — lines 8347–8354
<Button
type="button"
variant="primary"
className="w-full rounded-lg"
onClick={() => void submit()}
disabled={disabled || exportBusy}
>
{busy ? 'Deploying…' : '1‑Click Deploy (Gas-Free)'}
Disabled expression: disabled || exportBusy
disabled comes from disabledReason (7307–7327), which includes busy but not dryRunBusy:
const disabledReason =
busy
? 'Deployment in progress…'
: rolePolicyOverride.error
? rolePolicyOverride.error
: batcherInfraQueryLoading
? 'Reading deployment batcher infrastructure…'
: batcherInfraError
? batcherInfraError
: vanityPlanQueryLoading
? buildDeployVanityLoadingMessage({
vaultVanityPrefix: vaultVanityPrefix ?? null,
shareOftVanitySuffix: shareOftVanitySuffix ?? null,
})
: expectedAddressesQueryLoading
? 'Computing deployment addresses…'
: !expected
? expectedError || 'Deployment addresses are not ready.'
: null
const disabled = Boolean(disabledReason)
Contrast — dry-run / export buttons (lines 8141, 8150) use
disabled={busy || exportBusy || dryRunBusy || expectedQueryLoading || !expected}.
Contrast — exportPlan / runDryRun entry guards (7234, 7262):
if (busy || exportBusy || dryRunBusy) return
(c) dryRunBusy — declaration and setters
| Location | Code |
|---|---|
| 2580 (declare) | const [dryRunBusy, setDryRunBusy] = useState(false) |
| 7263 (set true) | setDryRunBusy(true) in runDryRun |
| 7284 (set false) | setDryRunBusy(false) in runDryRun finally |
const runDryRun = async () => {
if (busy || exportBusy || dryRunBusy) return
setDryRunBusy(true)
...
} finally {
setDryRunBusy(false)
DeployVault tests
No RTL/component tests for DeployVault.tsx (no @testing-library/react render of the page).
Related tests:
| File | What it tests |
|---|---|
/home/akitav2/projects/4626/frontend/src/pages/deploy/DeployVaultDryRun.test.ts | Source-string wiring (routes, env, dry-run copy) via fs.readFileSync on DeployVault.tsx |
deployVaultVanity.test.ts, deployVaultVanityPersistence.test.ts, deployVaultSignals.test.ts | Helper/signal modules, not the page component |
deployVaultGreenfieldProbe.test.ts | Greenfield probe lib |
H-3 — Swap auto-quote (Swap.tsx + useSwapExecution.ts)
(a) Auto-quote skip conditions — Swap.tsx 893–928
Effect early returns (before timer):
useEffect(() => {
...
if (!executionAddress || !quoteReady || quoteCooldownActive) return
if (txState === 'signing') return
if (tokenInAmountExceedsBalance) return
Inside 450ms timer callback:
const timer = window.setTimeout(() => {
if (busyRef.current === 'executeSwap') return
void handleQuote()
}, 450)
busyRef sync (897–898): busyRef.current = busy — busy is intentionally not in the effect deps (893–896 comment).
What pauses auto-quote today:
- Missing
executionAddressorquoteReady quoteCooldownActivetxState === 'signing'tokenInAmountExceedsBalance- At fire time only:
busy === 'executeSwap'
What does not pause auto-quote:
busy === 'quote' | 'review' | 'approval' | 'buildSwap' | …txState === 'pending' | 'review' | 'idle' | …(except signing)
Effect re-fires when inputs change: 916–927 (tokenIn, tokenOut, amountInUnits, slippage, executionAddress, quoteReady, quoteCooldownActive, tokenInAmountExceedsBalance, txState, handleQuote).
(b) quoteRunRef epoch-bumping and review bail
Declaration — line 803:
const quoteRunRef = useRef(0)
Bump on reset — line 1469:
const resetTradeState = useCallback(() => {
quoteRunRef.current += 1
handleQuote — capture + bail pattern (1520–1563):
const runId = ++quoteRunRef.current
setBusy('quote')
Stale-run checks at 1528, 1532, 1550, 1557; finally clears busy only if still current (1563):
} finally {
if (runId === quoteRunRef.current) setBusy(null)
handleReviewTrade — same pattern (1759+):
const runId = ++quoteRunRef.current
setBusy('review')
Bail checks at 1769, 1797, 1819, 1833, 1852, 1862, 1880, 1889; finally at 1893.
How review bails: Any later ++quoteRunRef.current (from handleQuote, handleReviewTrade, or resetTradeState) makes runId !== quoteRunRef.current, so review exits early at the next guard without updating state. A concurrent auto-quote handleQuote() therefore invalidates an in-flight review.
(c) Does handleReviewTrade guard on busy?
Partially — lines 1737–1739:
if (busy === 'executeSwap' || txState === 'signing' || txState === 'pending') {
setStatus('Swap already in progress. Wait for the current transaction to finish.')
return
Does not block when busy === 'quote' | 'review' | 'approval' | 'buildSwap' | 'executeApproval' | 'executeOrder'.
(d) txState values
Type definition — line 99:
type TxLifecycleState = 'idle' | 'review' | 'signing' | 'pending' | 'success' | 'error'
Initial state — line 763: useState<TxLifecycleState>('idle')
busy string values set in the hook
| Value | Line(s) |
|---|---|
'quote' | 1521 |
'approval' | 1612 |
'buildSwap' | 1681 |
'review' | 1760 |
'executeApproval' | 2063 |
'executeSwap' | 2137 |
'executeOrder' | 2801 |
Type: useState<string | null>(null) at 753.
useSwapExecution tests
File: /home/akitav2/projects/4626/frontend/src/hooks/useSwapExecution.test.ts
No hook mounting — no renderHook, no useSwapExecution() calls. Tests import exported pure helpers only:
import {
assertSwapSpendBalancePreflight,
deriveSwapExecutionReadiness,
evaluateCanonicalSubmitSession,
evaluateSwapSessionGate,
resolveCanonicalSubmitSession,
shouldDisablePermit2ForSwap,
shouldSimulateSwapBuild,
shouldSimulateSwapTransaction,
} from './useSwapExecution'
Describe blocks: evaluateSwapSessionGate, evaluateCanonicalSubmitSession, resolveCanonicalSubmitSession, CDP canonical mode policy helpers, deriveSwapExecutionReadiness, shouldDisablePermit2ForSwap, shouldSimulateSwapTransaction, shouldSimulateSwapBuild, assertSwapSpendBalancePreflight.
Setup pattern: direct function calls with inline mock objects (e.g. assertSwapSpendBalancePreflight passes { publicClient: { getBalance: async () => … } } at 409–419).
Related, not hook tests: swapTokenDecimals.test.ts imports assertSwapSpendBalancePreflight; useSwapState.test.ts tests swap state helpers. No Swap.tsx component tests found for the auto-quote effect.
Audit finding alignment (summary)
| Finding | Verified? |
|---|---|
H-2: submit() omits dryRunBusy; 1-Click button omits dryRunBusy while dry-run/export paths include it | Yes |
H-3: Auto-quote does not pause on review/quote busy; shared quoteRunRef can abort review; handleReviewTrade only guards executeSwap/signing/pending | Yes |
[REDACTED]
Was this page helpful?