Submit snapshot: debounce hybrid book leg, extend stale assert, consolidate buildSubmitAlignedSimPayload (#356 follow-up) #360
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#360
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Follow-up to #356. Pay-amount submit alignment landed via
useSubmitAlignedSimQuote,isSubmitQuoteStale(incl.isFetching), andassertSubmitQuotePayRawAlignedin Swap / Trade market mutations. Hybrid book-leg inputs are still live while pay is debounced, and the in-mutation assert does not cover them.buildSubmitAlignedSimPayloadis exported and tested but unused in production — the hook duplicates itsminReceivedbundling inline.Current codebase
What #356 already fixed (pay amount)
frontend-dapp/src/hooks/useSubmitAlignedSimQuote.ts— exposessubmitPayRaw(= debounced pay raw),simData,minReceived,isQuoteStale,isSubmitReady.frontend-dapp/src/utils/quoteDebounce.ts—isSubmitQuoteStale(raw, debouncedRaw, isPlaceholderData, isFetching);assertSubmitQuotePayRawAlignedchecks pay raw only.frontend-dapp/src/pages/SwapPage.tsxandfrontend-dapp/src/components/trade/TradeMarketOrderPanel.tsx—swapMutationusessubmitPayRaw/minReceived/simData; callsassertSubmitQuotePayRawAligned(rawInputAmount, debouncedRawInputAmount)at top ofmutationFn.Remaining gap — hybrid book leg (
bookInputHuman,hybridMaxMakers)queryFnhybrid splitisSubmitQuoteStale/ assertdebouncedRawInputAmountsimRawsubmitPayRaw(debounced)bookInputHuman)bookInputHumanbookInputHuman(Swap direct hybrid branch ~L654; Trade usesdebouncedHybridwith live book)hybridMaxMakersSwap (
SwapPage.tsx):simQueryKeyincludes livebookInputHumanandhybridMaxMakers(~L345–374).bookInputHumanagainst debouncedsimRaw.swapMutationdirect hybrid fallback (~L652–667) recomputesbookRawfrom livebookInputHuman+ debouncedpayRaw— not snapshotted with the sim that producedminReceived/idxOps.directHybridBookSplitdisplay (~L756–766) uses liverawInputAmount+ livebookInputHuman.Trade market (
TradeMarketOrderPanel.tsx):simQueryKeyincludes livebookInputHuman(~L228–238).debouncedHybrid=computeHybridParams(debouncedRawInputAmount, …, bookInputHuman, …)— debounced pay + live book (~L159–161).debouncedHybrid(~L375) but stale gate / assert do not verify book leg or max makers match the settled sim key.liveHybrid/ gas estimate still uses live pay + live book (~L154–166) — intentional for UX hints, but must not be the submit snapshot.Dead / duplicated helper
buildSubmitAlignedSimPayload(~L40–52) bundles{ payRaw, minReceived, simData, indexerOperations }but is only imported inquoteDebounce.test.ts.useSubmitAlignedSimQuoterecomputesminReceivedviaapplySlippagePercentFloorinstead of callingbuildSubmitAlignedSimPayload— two sources of truth for the same snapshot shape.Why this is needed
assertSubmitQuotePayRawAlignedonly guards pay raw. A programmatic or race bypass of the UI stale gate could submit a different book split than the quote used forminReceived/ hybridmin_return— same class of slippage / revert risk as the original #356 pay-amount skew.rawInputAmount === debouncedRawInputAmount) but the user edits the book leg or max makers, submit may stay enabled whilekeepPreviousDatashows a quote for the previous hybrid params (only indirectly blocked ifisFetchinghappens to be true).buildSubmitAlignedSimPayloadinto the hook (single snapshot builder) or remove the dead export to avoid drift between test-only and production paths.Constraints and guardrails
SIM_QUOTE_DEBOUNCE_MS(350) unless product explicitly wants a different constant — document if changed.getDirectHybridBookSplit/computeHybridParams/swapDisclosurehelpers stay pure; extend or wrap them rather than duplicating split math in components.debouncedHybridfor on-chainhybridparams (already partially correct) — extend snapshot, don't revert to live pay.liveHybridmay remain for gas / max-spend UX; only submit and quote display when submit-ready must use the snapshotted hybrid params.isSimQuoteStaleForSubmitdeprecated alias until call sites are migrated.skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.mdanddocs/frontend.md#356 sections when behavior changes.Relevant files
frontend-dapp/src/hooks/useSubmitAlignedSimQuote.ts,.test.tsfrontend-dapp/src/utils/quoteDebounce.ts,.test.tsfrontend-dapp/src/hooks/useDebouncedValue.tsfrontend-dapp/src/pages/SwapPage.tsx,SwapPage.test.tsxfrontend-dapp/src/components/trade/TradeMarketOrderPanel.tsxfrontend-dapp/src/utils/swapDisclosure.ts,.test.tsdocs/frontend.md,skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.mdRecommended direction
bookInputHuman(and optionally debouncedhybridMaxMakersor treat max-makers as immediate with explicit stale on change):const debouncedBookInputHuman = useDebouncedValue(bookInputHuman, SIM_QUOTE_DEBOUNCE_MS)simQueryKey/queryFninstead of live values.isSubmitQuoteStale(or addisHybridParamsStale) to return true when live book ≠ debounced book or live max makers ≠ snapshotted max makers (mirror pay-amount pattern).assertSubmitHybridAligned({ bookLive, bookDebounced, maxMakersLive, maxMakersSnap })called alongsideassertSubmitQuotePayRawAligned; Swap direct hybrid branch must read book split from snapshot, not closurebookInputHuman.useSubmitAlignedSimQuotecallbuildSubmitAlignedSimPayloadforminReceived+ expose fullsubmitPayload, orbuildSubmitAlignedSimPayload+SubmitAlignedSimPayloadtype fromquoteDebounce.ts(update tests to target the hook).submitHybrid: HybridSwapParams | undefinedfrom the hook so Swap and Trade mutations stop recomputing splits inline.Acceptance criteria
minReceived,indexerOperations, and on-chainhybridparams all derive from the same settled sim key (debounced pay + debounced book [+ snapshotted max makers]).isSubmitReadyis false while live book leg ≠ debounced book leg (or while hybrid params are otherwise stale), even if pay raw matches debounced pay.mutationFnasserts hybrid alignment before broadcast (defense in depth); mismatch throws the same class of user-facing error as pay misalignment.bookInputHumanin submit path.buildSubmitAlignedSimPayloadis either used byuseSubmitAlignedSimQuote(single builder) or removed with tests migrated — no orphaned export.Test plan (all paths)
Unit —
quoteDebounce.tsbuildSubmitAlignedSimPayloadwired or removed per chosen direction.Unit —
useSubmitAlignedSimQuoteisSubmitReadyfalse when book leg stale.submitPayRaw+ exposed hybrid snapshot match debounced inputs.Unit —
swapDisclosure.ts/computeHybridParamsComponent —
SwapPage.test.tsxswap/postRouteSolveargs).Component — Trade market panel (add or extend tests)
debouncedHybridmatching sim key.Integration / manual (LocalTerra +
make dev)10, book2→ wait for quote → change book to5→ submit disabled until refresh → submit sendsbook_inputmatching displayed quote.max maker fillswith stable pay/book → submit disabled during refetch.Automated commands
Test plan — attack, hack, and abuse vectors
keepPreviousDatamin_returnmatches quoted outswapMutationwhile hybrid staleassertSubmitHybridAlignedthrows before broadcastpostRouteSolvevssimulateHybridSwapfallbackVerification criteria
mainbranch after merge.make test-frontendgreen.docs/frontend.mdandskills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md#356 rows mention debounced book leg.bookInputHumaninsideswapMutationhybrid branches (Swap + Trade).rg buildSubmitAlignedSimPayload frontend-dapp/srcshows only intentional usages (hook or tests), not dead export.Related
skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md— submit–quote alignment tablemarked as related to #356
mentioned in commit
c1bea72d0cmentioned in merge request !866
mentioned in commit
16e0656c37mentioned in merge request !868
Heads up — #360 (!866) merges clean but left
npm run build(tsc -b) red on main at16e0656.buildSubmitAlignedSimPayloadtypes itsapplySlippageFloorparam as=> string, but the fnuseSubmitAlignedSimQuoteactually passes isapplySlippagePercentFloor, which returnsstring | null. TheminReceivedfield it feeds is alreadystring | nulland every consumer (SwapPage, TradeMarketOrderPanel) null-checks it — so the param type was just too narrow.Fix is a one-line widen to
=> string | nullinqa/360-build-tsc-minreceived-null-> MR !868. Build green after it, full vitest 935/937 (the 2 fails are the known terraExplorer localhost-vs-127.0.0.1 env artifact, unrelated).Same CI gap as before: the pipeline runs vitest + eslint but not tsc -b, so type-only breaks keep landing on main unseen. Might be worth a tsc -b step.
mentioned in commit
f0a1a65bb7mentioned in commit
8408689ee5Verified the implementation (
c1bea72/ !866) at the source + unit layer on16e0656— all six acceptance criteria map:37 targeted tests green (quoteDebounce, useSubmitAlignedSimQuote, swapDisclosure, SwapPage, TradeMarketOrderPanel.submitSnapshot). The new snapshot test fails against the pre-fix panel (book 2->5 leaves submit enabled) and passes at HEAD, so it genuinely guards the AC2 behavior.
Two things before this closes: (1) the tsc -b build break flagged above needs !868 merged first; (2) the 4 live LocalTerra hybrid scenarios + the on-chain min_return match are browser/laptop — on my queue. Good to close from my side on the src+unit layer once !868 lands and the laptop hybrid pass rides along.
mentioned in issue #337
Verify #360 — PASS (main @
8408689)Verified on
origin/mainafter fast-forward to8408689(includesc1bea72#360 implementation +f0a1a65tscminReceivednull widen from !868).Acceptance criteria
debouncedBookInputHuman+debouncedHybridMaxMakersin sim keys;submitDirectHybrid/debouncedHybridfor mutations (SwapPage.tsx,TradeMarketOrderPanel.tsx)isSubmitReadyfalse when live book ≠ debounced (pay stable)isSubmitQuoteStalehybrid branch; hook + component testsmutationFnasserts hybrid alignment before broadcastassertSubmitHybridAlignedin Swap + TradeswapMutationbookInputHumanin hybrid tx build)submitDirectHybridfrom debounced inputs; live book only in stale assertbuildSubmitAlignedSimPayloadwired (no orphan export)useSubmitAlignedSimQuotecalls builder;rg→ hook + tests onlydocs/frontend.md,skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md#356/#360 rowsAutomated tests
Manual / LocalTerra
Provisioned stack via
make setup-cloud-localterra(chain block ~5351, indexer :3001 healthy).SwapPage.test.tsx"book leg differs from debounced hybrid quote (#360)"TradeMarketOrderPanel.submitSnapshot.test.tsxquoteDebounce.test.tsmax-makers stale caseSwapPageoutage/fallback tests unchangedAd-hoc Playwright on LocalTerra failed to locate Simulated Wallet connect control (harness/fixture gap in one-off script, not stale-gate regression). Full hybrid on-chain E2E (
hybrid-swap.spec.ts) not re-run in this verify session due to time; component tests explicitly guard AC2.Verification checklist
make test-frontendgreenbookInputHumanin submit hybrid branches (assert-only for defense in depth)buildSubmitAlignedSimPayloadintentionally usedNote (out of #360 scope)
npm run build(tsc -b) still fails on main with errors interraWalletSignTxRaw.ts/terraTxHash.ts(#359 broadcast recovery) — unrelated to the #360string | nullslippage-floor fix. CI does not runtsc -btoday.Follow-up ideas
connectWalletfixture (mirror component tests).tsc -bto frontend CI to catch type-only breaks.mentioned in issue #361
mentioned in commit
26a4b7b6a2mentioned in merge request !894
mentioned in issue #428
mentioned in merge request !1041