Raw contract error ("Max spread assertion") surfaced directly in UI on failed swap. no human-readable fallback #134
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#134
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
Attempting to swap CORAL for JADE fails on-chain with a
Max spread assertionerror. The contract rejected the swap because the actual spread (96.94%) exceeded the pool's maximum allowed spread (1%). The frontend submitted the transaction without surfacing any price impact warning, leaving the user with a failed tx and no clear explanation of why.Reproduction Steps
Expected Behavior
Before submitting, the UI should calculate and prominently display the price impact / spread for the swap. If the spread exceeds a safe threshold (e.g. >5%), a high-impact warning should be shown. If the spread would breach the contract's max spread (1%), the submit button should be disabled or the user should be blocked with a clear human-readable explanation: "Insufficient liquidity price impact too high for this trade size."
Actual Behavior
The transaction was submitted and rejected on-chain. The UI displayed the following raw contract error verbatim to the user:
No price impact warning was shown before signing. The raw on-chain error string — including internal dispatch chain, numeric spread value, and wasm execution context — was passed through directly to the UI with no translation or human-readable fallback.
Environment Details
localterra(local Docker)VITE_NETWORK=local npm run devmake indexer-dev)make deploy-localWallet / Device Details
http://localhost:1317, RPChttp://localhost:26657Console Logs
Screenshot
Severity / Impact
Nit. No funds were lost the transaction was rejected before any token transfer.
Max spread assertion: actual spread (0.969...)) is not human-readable; should be translated to something like "Trade size too large for available liquidity try a smaller amount"Likely affects all thin-liquidity pairs, not just CORAL/JADE. Related to checklist items W7-C6 (slippage/price impact panel) and W11-C1 (error copy quality).
cc: @PlasticDigits
mentioned in commit
2683861722Update (merged to `main`)
Implemented GitLab #134: multihop / indexer / native-router swap quotes now run a sequential per-hop pair preflight (factory + `simulation` / `hybrid_simulation`) so price impact matches pair `assert_max_spread` (same formula as on-chain). Submit is blocked when any hop would exceed the user’s Slippage tolerance (`max_spread`). Raw `Max spread assertion` LCD logs are mapped to short retail copy in `humanizeTerraTxError.ts`.
Docs: `docs/swap-max-spread-ux.md` · `docs/frontend.md` § Swap · crosslinks in `skills/AGENTS_LOCALNET_TRADING_SWARM.md`, `AGENTS_TERRACLASSIC_GAS.md`, `AGENTS_FRONTEND_PRODUCTION_BUILD.md`.
Commit: `
2683861` on `main`.Verification checklist
@totdking — could you verify on your CORAL → JADE LocalTerra repro when you have a moment? Leaving this issue open until you sign off.
Formatting note: previous comment used escaped backticks; key links are docs/swap-max-spread-ux.md on main and commit
2683861.mentioned in commit
c4bc1c2b55mentioned in issue #135
mentioned in commit
4bfcced796@totdking @PlasticDigits — source-level audit found additional raw chain errors that bubble through to the UI without humanization, beyond the Max spread case this ticket covers. flagging here in case scope expansion is preferred over new tickets — defer to your call.
Patterns currently raw in
transactions.ts:264humanizeTerraTxError.ts:22-35only matches Max spread assertion (this ticket) and "LimitOrder ... not found" (#135). Everything else falls through to:Real-world contract error patterns that surface raw to retail users today:
Contract is paused— pairassert_not_pausedrejection. e.g., during emergency pause; see DEX #120 thread.Unauthorized— admin-gated entrypoint hit by non-admin senderInsufficient funds— wallet doesn't have enough native LUNC for feesout of gas— gas estimate undershoots actual usage. related to #115 / #127 history.deadline exceeded— assert_deadline rejection on expired txInvariantViolation: pending escrow ...— pair contract invariant violationswasm Std generic_errstringsSuggestion
either:
A. expand
humanizeTerraTxError.tshere — add classifier branches for the patterns above, keep this ticket as the umbrellaB. close this ticket on Max spread sign-off and file a new issue for the remaining patterns
either works for me. flagging since the architectural pattern (single classifier in
humanizeTerraTxError.ts) is the right scaling shape for option A.related side-finding
TxResultAlert.tsx:14-16renders{message}directly with no humanizer step. there is currently no enforcement that callers pass humanized strings — any new caller will silently regress. consider adding a humanize step insideTxResultAlertitself, or a lint rule. that goes beyond this ticket scope but worth noting.cc @PlasticDigits
mentioned in issue #145
Expand humanizeTerraTxError is approved.
TxResultAlert add humanize step is approved & should be in a new issue.
mentioned in commit
74e705a5ee@totdking — humanizer expansion shipped for verification, per dev's 5/07 sign-off on the audit note ("Expand humanizeTerraTxError is approved").
what changed
tryHumanizeTerraTxMessageinfrontend-dapp/src/utils/humanizeTerraTxError.tsnow classifies 6 additional raw chain-error patterns that previously bubbled through to the UI as raw dispatch dumps:assert_not_paused/ generic "contract is paused") -> "This pool is currently paused by the operator. Try again later or pick a different pair."each pattern uses a tight regex with case-insensitive matching.
\bUnauthorized\buses a word boundary specifically to avoid false-matching on substrings like "pre-authorization not found".existing branches (Max spread assertion, LimitOrder map key not found) untouched, with new regression tests.
test coverage
frontend-dapp/src/utils/__tests__/humanizeTerraTxError.test.ts— 19 unit tests covering every branch (existing + new), false-match guards, and passthrough cases (unrecognized errors, empty string, bare "Transaction failed:")frontend-dapp/src/services/terraclassic/__tests__/transactions.test.ts:79-91— the existing "throws when txResponse.code is non-zero" test used a raw 'out of gas' rawLog as the test fixture. with the new humanizer that pattern now produces "Transaction needed more gas than estimated..." so the assertion was updated to match the humanized output. test intent (non-zero code -> throws) preserved.verification needed
fix/glab-134-humanize-additional-chain-errors74e705ahow to verify
git checkout fix/glab-134-humanize-additional-chain-errorscd frontend-dapp && npm install && npm run devverification gate (already passing on my side)
tsc -bcleannpm run test:unit— 334/334 PASS (44 files), up from 315 previously: +19 new humanizer testsnpm run lint— 0 errors. 3 pre-existing warnings onLimitOrdersPage.tsx(untouched)scope notes
ping when verified, i'll open the MR after your sign-off.
mentioned in commit
ecdc2723c9Merged, ready for verification @totdking
Verification checklist
Issues noticed
The wallet tx swap fee approx. goes down from 36 to 23 lunc which fails with error:
Transaction needed more gas than estimated. Try again — gas usage can vary slightly between blocks.This consumes the gas, but the tx fails still.This only fails when the tx fee is reduced and works well at the initial 36 lunc fee.
cc: @PlasticDigits
mentioned in commit
6c148e8252mentioned in commit
19766b9cd7Update (merged to
main)Follow-up for @totdking’s remaining checklist items on #134:
Commit:
6c148e8onmain.What changed
Forced on-chain Max spread → retail copy
terraBroadcast.tsnow throws humanizedTrade rejected: price impact…directly (no doubleTransaction failed:prefix).TxResultAlert+humanizeUserFacingErrorregression tests cover the full wasm log shape.Wallet fee ~36 → ~23 LUNC →
out of gassend→swap(and top-levelswap) gas now uses the same 830k buffered envelope as one-hopexecute_swap_operations(was 600k, below #115 observed ~753k usage).extensionSignedFeeGuard.ts+ cosmesKeplrExtensionpatch) so Station cannot halve the signed envelope and still pass.packages/localnet-trading-swarm/src/gas.tskept in sync.Docs / agents:
docs/swap-max-spread-ux.md·docs/frontend.md§ Swap ·skills/AGENTS_TERRACLASSIC_GAS.md·skills/AGENTS_LOCALNET_TRADING_SWARM.mdTests:
npm run test:unit— 542/542 PASS.Verification checklist
max_spread) → alert shows Trade rejected: price impact… (not the wasm dispatch stack).npm ci).@brouie — please verify the two items above on LocalTerra when you can. Leaving #134 open for @totdking sign-off.
mentioned in issue #138
Verification checklist
max_spread) → alert shows Trade rejected: price impact… (not the wasm dispatch stack).npm ci).Good to close on this end .
cc: @PlasticDigits
mentioned in issue #412
mentioned in issue #414
mentioned in issue #429
mentioned in issue #460
mentioned in issue #475
mentioned in issue #595
mentioned in issue #678
marked as related to #678