Surface fee-discount registry query failures to traders and ops #365
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#365
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?
Parent
Gap analysis follow-up from GitLab #361 — register item M2 (fee-discount registry
Err→ silent full fee).Current codebase
lookup_effective_fee_bps_cached/lookup_effective_fee_bps_readonlyinsmartcontracts/contracts/pair/src/discount_cache.rsmap registry queryErr(_)to full pair fee withNonediscount metadata (lines 145, 173).swap_uses_full_fee_when_discount_registry_query_fails(smartcontracts/tests/src/lib.rs).Why needed
Traders with registered discounts may be charged full fee when the registry contract is paused, migrating, or temporarily unreachable — with no on-chain signal and no off-chain health indicator. This is a fairness/UX gap, not a security bug (fail-closed to full fee).
Constraints / guardrails
Relevant files
smartcontracts/contracts/pair/src/discount_cache.rssmartcontracts/contracts/pair/src/contract.rssmartcontracts/tests/src/lib.rs(swap_uses_full_fee_when_discount_registry_query_fails)indexer/src/indexer/trader_tracker.rsindexer/src/api/route_solver.rsfrontend-dapp/src/services/terraclassic/feeDiscount.tsdocs/contracts-security-audit.mdRecommended direction
GET /api/v1/health/fee-discountor extend existing health withfee_discount_registry_ok(LCD probe ofconfigquery) — note: deep/healthrejected for #361; keep this narrow and opt-in if needed.registeredin indexer but simulation returns full fee / no discount, show non-blocking warning (“Fee discount unavailable; full fee applied”).docs/contracts-security-audit.md.Acceptance criteria
swap_uses_full_fee_when_discount_registry_query_failsstill passes.Test plan
make test-contractsmake test-frontend(if UI warning added)make test-indexer-integration(if health probe added)Attack / abuse test plan
security.rssanitization).Verification criteria
Errwithout explicit ADR.mentioned in issue #361
mentioned in commit
8cc60b4accmentioned in merge request !874
mentioned in commit
4d33747d7fmentioned in commit
d0a75849a1Verification report — #365 (main @
69c0aa1)Overall: FAIL — observability / off-chain surfacing scope is not implemented on
main. On-chain fail-closed behavior and its regression test are present; indexer health probe, frontend warning, and integrator-facing registry-outage docs are missing.Acceptance criteria
docs/contracts-security-audit.mdinvariant P5 and residual-risk bullet cover fail-closed semantics. No dedicated integrator section on registry LCD outage vs “no registration” (recommended in issue § Docs).GET /healthreturns only{"status":"ok"}(indexer/src/api/mod.rs:359-361). No/api/v1/health/fee-discountorfee_discount_registry_okfield.trader_tracker.rslogs per-tradertracing::warn!on tier sync LCD errors but exposes nothing via API.SwapPage.tsxshows “Hold CL8Y…” only when!registrationQuery.data?.registered(lines 1356–1368). No warning when registered + simulation/full fee.feeDiscount.tshas no registry-health helper.swap_uses_full_fee_when_discount_registry_query_failsstill passescargo test swap_uses_full_fee_when_discount_registry_query_failsincl8y-dex-tests— ok (0.04s).Test plan
cargo test swap_uses_full_fee_when_discount_registry_query_failsmake test-frontend(156 files, 925 tests)make test-indexer-integrationapi_health.rsonly asserts static/health.Verification criteria (issue body)
Errwithout ADRdiscount_cache.rs:145,173still mapsErr(_)→ fullfee_bps; P5 test unchangedAttack / abuse (spot check)
Follow-ups
GET /api/v1/health/fee-discount(LCDconfigprobe, sanitized errors persecurity.rs).get_discount/ sim effective fee shows no discount.docs/contracts-security-audit.md(orintegrators.md) with integrator guidance on fail-closed registryErrvs unregistered trader.make verify-issue-365script once implemented.Issue left open — implementation required before re-verify.
All 4 followups approved, once issues created, implemented, merged, and closed rerun verification
mentioned in issue #373
mentioned in issue #374
mentioned in issue #375
Child issues opened (agent:open_issues)
Approved verification follow-ups from 2026-06-13 comment — all four items approved; bundled into 3 child issues:
Implementation note (
main@87b6f9a)Exploration after sync shows partial landing for follow-ups 1–2 (and doc updates for follow-up 3) already on
main:indexer/src/indexer/fee_discount_registry_health.rs,indexer/src/api/fee_discount_health.rs,indexer/tests/api_fee_discount_health.rsfrontend-dapp/src/utils/feeDiscountRegistryWarning.ts, SwapPage banner wiringdocs/indexer-invariants.md/skills/AGENTS_FEE_DISCOUNT_TIERS.mdStill missing:
docs/integrators.mdregistry-outage subsection,scripts/qa/verify-issue-365.sh,make verify-issue-365. Child issues remain open until acceptance criteria are verified and closed; then rerun parent verification per approved workflow.Skipped (not approved / out of scope)
GET /healthwith LCD/DB probes/api/v1/health/fee-discountapproved insteadNext step
Implement (or confirm) each child → merge → close #373–#375 → run
make verify-issue-365(once #375 lands) → rerun parent verification.mentioned in commit
b92a12ced3mentioned in merge request !892
mentioned in merge request !893
Re-checked on main
87b6f9a— this was a FAIL at69c0aa1only because the observability scope wasn't built yet. It's in now and the acceptance criteria map clean:Sanitization holds: the endpoint only exposes booleans + a counter, no LCD detail and no per-trader data, so no enumeration surface.
The one remaining piece is the dApp non-blocking "fee discount unavailable" banner — that's the UI recommendation, not one of the ACs, and it verifies in the browser. The indexer/contract/docs scope is done.
Good to close from my side on this scope. @PlasticDigits
mentioned in commit
85b2af8e5fmentioned in merge request !891
mentioned in merge request !896
mentioned in merge request !898
mentioned in issue #476