Post-limit-placement cancel ID polling silently blocked by CORS; cancel field never auto-fills #131
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#131
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?
Issue Summary
After a successful limit order placement, the frontend polls the indexer to retrieve the new order ID and auto-fill the cancel field. All poll requests are blocked by the browser's CORS policy due to a host mismatch between the indexer's allowed origin (
http://localhost:5173) and the indexer URL used in the app (http://127.0.0.1:3001). Thecatch {}block discards all errors silently, so the cancel field stays empty and the user cannot cancel their live order without manually locating the order ID.Reproduction Steps
/limits, select a pair, enter a valid amount, click Place Limitlimit-placements— observenet::ERR_FAILED 200 (OK)on every poll requestExpected Behavior
After a successful limit placement, the frontend should retrieve the new order ID from the indexer and auto-fill the cancel field so the user can immediately cancel if needed.
Actual Behavior
Every poll request to
http://127.0.0.1:3001/api/v1/pairs/<pair>/limit-placements?limit=100is blocked by CORS. The browser receives a 200 response but rejects it because theAccess-Control-Allow-Originheader (http://localhost:5173) does not match the127.0.0.1host. Thecatch {}block inLimitOrdersPage.tsx:354discards all errors silently. The cancel field is never populated.Console error observed:
Workaround: manually open
http://127.0.0.1:3001/api/v1/pairs/<pair_address>/limit-placements?limit=100in the browser, filter by your wallet address, and paste theorder_idinto the cancel field.Screenshots
Root Cause
VITE_INDEXER_URL=http://127.0.0.1:3001in.env.localand the indexer'sCORS_ORIGINSdefault ofhttp://localhost:5173(config.rs:267) are mismatched. The browser treatslocalhostand127.0.0.1as different origins and blocks the response even though the server returns 200.Relevant files:
frontend-dapp/.env.local—VITE_INDEXER_URL=http://127.0.0.1:3001indexer/src/config.rs:267— local devCORS_ORIGINSdefaultfrontend-dapp/src/pages/LimitOrdersPage.tsx:354— silentcatch {}hides CORS errorsEnvironment Details
localterra(local Docker)VITE_NETWORK=local npm run devmake indexer-dev)make deploy-localWallet / Device Details
http://localhost:26657Severity / Impact
local dev only. No production impact.
Production uses real domain names; the
localhostvs127.0.0.1ambiguity does not exist there. In local dev, this silently breaks the post-placement UX on every session. The user cannot cancel a live limit order without manually querying the indexer.CC: @PlasticDigits
mentioned in issue #133
@PlasticDigits — adding context on this one. the bare
catchin the post-place poll is the proximate reason the CORS error stayed invisible. there is no console signal, so any failure mode (CORS misconfig, indexer 5xx, JSON parse error, DNS) all look identical to "indexer not running locally" — which is the comment intent.specifically:
LimitOrdersPage.tsx:148-150TradeOrderTicket.tsx:131-133has the same pattern.minimum visibility fix is one line per site:
catch (err) { console.warn("[limit-place] indexer poll failed:", err) }. that would not have prevented the CORS misconfig but it would have surfaced the failure in devtools immediately rather than requiring a network-tab inspection to find it.a heavier option would be a one-time toast after N consecutive failures of the same shape ("Indexer unreachable, manual order ID lookup required") so non-power-users get a hint that the cancel-id field is not going to populate. but that is belt-and-suspenders on top of the actual CORS fix you would already be making.
the underlying fix for #131 itself is still the localhost vs 127.0.0.1 origin mismatch —
VITE_INDEXER_URLandCORS_ORIGINSneed to agree. just wanted to flag that even after that is fixed, the silent catch will keep masking future issues unless the logging goes in.OK, we need to make sure we have full logging
mentioned in commit
3837558ca8mentioned in commit
454bad0f7fLanded on
main(3837558+454bad0)What changed
indexer/.env.examplenow lists bothhttp://localhost:5173andhttp://127.0.0.1:5173(plus matching:4173preview origins) inCORS_ORIGINS, aligned withscripts/deploy-dex-local.shso copy-paste local setups do not trip cross-host CORS when Vite is opened via127.0.0.1.warnIndexerPlacementPollFailedlogs[limit-place] indexer poll failed:from LimitOrdersPage and TradeOrderTicket post-place polls (Vitest inwarnIndexerPlacementPollFailed.test.ts).docs/frontend.md§ Local dev indexer CORS,docs/indexer-invariants.md§ Local dev CORS (stable anchor),docs/environment-matrix.md,docs/limit-orders.md,docs/testing.md,frontend-dapp/.env.examplecomment;skills/AGENTS_LOCALNET_TRADING_SWARM.md+skills/AGENTS_FRONTEND_PRODUCTION_BUILD.md.Invariant (local dev)
Browsers treat
localhostand127.0.0.1as different origins. IndexerAccess-Control-Allow-Originmust echo the pageOrigin; listing only one hostname breaksfetchfrom the other even when the HTTP status is 200.Verification checklist
indexer/.env.example→ local indexer env (or merge the newCORS_ORIGINSline); restart indexer.http://127.0.0.1:5173, place a limit with indexer up → Cancel Order ID auto-fills after indexer catches the placement (no CORS errors in console).http://localhost:5173→ same behavior.CORS_ORIGINS(only one hostname) → console shows[limit-place] indexer poll failed:while polling (DevTools visible signal).cd frontend-dapp && npm ci && npm run test:runpasses.@Requesting verification from @totdking — please confirm on your macOS/Chrome LocalTerra setup; leaving this issue open until you sign off.
Verification checklist
indexer/.env.example→ local indexer env (or merge the newCORS_ORIGINSline); restart indexer.http://127.0.0.1:5173, place a limit with indexer up → Cancel Order ID auto-fills after indexer catches the placement (no CORS errors in console). (Actual port is 3000, not 5173 , vite.config.ts hardcodes port: 3000)http://localhost:5173→ same behavior. (Actual port is 3000, not 5173 , vite.config.ts hardcodes port: 3000)CORS_ORIGINS(only one hostname) → console shows[limit-place] indexer poll failed:while polling (DevTools visible signal).cd frontend-dapp && npm ci && npm run test:runpasses.Visual checklist
cancel ID polling after limit placement.
Observed: I firstly "delibrately misconfiguring"
CORS_ORIGINS((CORS_ORIGINS="http://localhost:3000" cargo run). Normal make indexer-dev resolves the cancel ID correctly.),Then placing a limit order, the frontend polls the indexer for the new order ID. This poll is being blocked by CORS in the browser:
The indexer returns a valid 200 response but the browser blocks the frontend from reading it because http://127.0.0.1:3000 is not in the indexer's CORS_ORIGINS. The default in config.rs:267 only allows http://localhost:5173, neither the actual frontend port (3000) nor
the 127.0.0.1 hostname are covered.
The warnIndexerPlacementPollFailed.ts logging is working the failure is now visible in console instead of silent.
Fix needed: add http://127.0.0.1:3000 and http://localhost:3000 to CORS_ORIGINS defaults in config.rs:267.
Environment: Chrome, 1470×864, Station wallet, LocalTerra local stack.
cc : @PlasticDigits
Great, thanks for testing the cors via misconfiguration. Clsoing, cors origins should not have localhost defaults, should require setting cors properly
mentioned in issue #337
mentioned in issue #629