Raw contract error ("Max spread assertion") surfaced directly in UI on failed swap. no human-readable fallback #134

Closed
opened 2026-05-06 14:20:31 +00:00 by totdking · 28 comments
totdking commented 2026-05-06 14:20:31 +00:00 (Migrated from gitlab.com)
No description provided.
totdking commented 2026-05-06 14:25:08 +00:00 (Migrated from gitlab.com)

Issue Summary

Attempting to swap CORAL for JADE fails on-chain with a Max spread assertion error. 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

  1. Complete full local setup (LocalTerra running, contracts deployed, indexer running, frontend running)
  2. Connect wallet with CORAL balance
  3. Navigate to the Swap tab and select CORAL → JADE pair
  4. Enter a swap amount
  5. Click Swap and approve the transaction in the wallet
  6. Observe the transaction fails on-chain

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:

Transaction failed: Transaction failed: failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: dispatch: submessages: Max spread assertion: actual spread (0.969444373510098454) exceeds max allowed (0.01): execute wasm contract failed

Field Value
Actual spread 96.94%
Max allowed spread 1% (0.01)

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

Field Value
OS macOS (Apple M1 Pro, 14-inch)
Browser Google Chrome (desktop)
Network localterra (local Docker)
Frontend VITE_NETWORK=local npm run dev
Indexer Running (make indexer-dev)
Contracts Deployed via make deploy-local

Wallet / Device Details

  • Wallet: Simulated dev wallet & keplr browser extension
  • Network configured in Station: localterra : LCD http://localhost:1317, RPC http://localhost:26657
  • Token pair tested: CORAL → JADE

Console Logs

transactions.ts:158 transaction error: Error: Transaction failed: failed to execute message; 
message index: 0: dispatch: submessages: dispatch: submessages: dispatch: submessages: 
Max spread assertion: actual spread (0.969444373510098454) exceeds max allowed (0.005): 
execute wasm contract failed
    at executeTerraContract (transactions.ts:158:13)

Screenshot

Screenshot 2026-05-06 at 15.20.12.png{width=900 height=545}


Severity / Impact

Nit. No funds were lost the transaction was rejected before any token transfer.

  1. Missing pre-trade price impact calculation : the swap form (W7-C6) does not compute or display spread/impact before submission, so the user has no signal that the trade will fail
  2. Poor error UX : the raw contract error copy (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

### Issue Summary Attempting to swap CORAL for JADE fails on-chain with a `Max spread assertion` error. 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 1. Complete full local setup (LocalTerra running, contracts deployed, indexer running, frontend running) 2. Connect wallet with CORAL balance 3. Navigate to the Swap tab and select CORAL → JADE pair 4. Enter a swap amount 5. Click **Swap** and approve the transaction in the wallet 6. Observe the transaction fails on-chain --- ### 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: > `Transaction failed: Transaction failed: failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: dispatch: submessages: Max spread assertion: actual spread (0.969444373510098454) exceeds max allowed (0.01): execute wasm contract failed` | Field | Value | |-------|-------| | Actual spread | 96.94% | | Max allowed spread | 1% (0.01) | 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 | Field | Value | |-------|-------| | OS | macOS (Apple M1 Pro, 14-inch) | | Browser | Google Chrome (desktop) | | Network | `localterra` (local Docker) | | Frontend | `VITE_NETWORK=local npm run dev` | | Indexer | Running (`make indexer-dev`) | | Contracts | Deployed via `make deploy-local` | --- ### Wallet / Device Details - **Wallet:** Simulated dev wallet & keplr browser extension - **Network configured in Station:** localterra : LCD `http://localhost:1317`, RPC `http://localhost:26657` - **Token pair tested:** CORAL → JADE --- ### Console Logs ``` transactions.ts:158 transaction error: Error: Transaction failed: failed to execute message; message index: 0: dispatch: submessages: dispatch: submessages: dispatch: submessages: Max spread assertion: actual spread (0.969444373510098454) exceeds max allowed (0.005): execute wasm contract failed at executeTerraContract (transactions.ts:158:13) ``` --- ### Screenshot ![Screenshot 2026-05-06 at 15.20.12.png](/uploads/5a662ea40be11c05a3bee6e7130e30c9/Screenshot_2026-05-06_at_15.20.12.png){width=900 height=545} --- ### Severity / Impact **Nit.** No funds were lost the transaction was rejected before any token transfer. 1. **Missing pre-trade price impact calculation** : the swap form (W7-C6) does not compute or display spread/impact before submission, so the user has no signal that the trade will fail 2. **Poor error UX** : the raw contract error copy (`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
PlasticDigits commented 2026-05-06 14:44:26 +00:00 (Migrated from gitlab.com)

mentioned in commit 2683861722

mentioned in commit 26838617220dee2844c540987bf4fb8bb201759a
PlasticDigits commented 2026-05-06 14:45:04 +00:00 (Migrated from gitlab.com)

Update (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

  • LocalTerra thin pair: pick two CW20s with very shallow liquidity, enter a large swap amount → Price impact shows a high worst-hop % (not 0%), Route spread check card appears, and Swap is disabled with Price impact too high for this trade / insufficient-liquidity messaging when impact exceeds slippage.
  • Slippage change: with the same amount, open Settings and lower/raise slippage → quote refetches and the disabled state toggles consistently with tolerance.
  • Multihop (client BFS): if you have a 2+ hop CW20 route, confirm the same preflight card and impact line behave sensibly.
  • Indexer route: CW20→CW20 with indexer hybrid enabled (default path) still quotes and blocks correctly on extreme sizes.
  • Forced on-chain failure (optional): if you bypass UI with an old build or custom max_spread, confirm a `Max spread assertion` log surfaces as the Trade rejected: price impact… sentence (not the full wasm stack).
  • Regression: normal deep-liquidity swap still submits; direct single-hop pool swap still shows impact from pair `simulation` as before.

@totdking — could you verify on your CORAL → JADE LocalTerra repro when you have a moment? Leaving this issue open until you sign off.

## Update (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\`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/swap-max-spread-ux.md) · [\`docs/frontend.md\` § Swap](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/frontend.md) · crosslinks in \`skills/AGENTS_LOCALNET_TRADING_SWARM.md\`, \`AGENTS_TERRACLASSIC_GAS.md\`, \`AGENTS_FRONTEND_PRODUCTION_BUILD.md\`. **Commit:** \`2683861\` on \`main\`. --- ### Verification checklist - [ ] **LocalTerra thin pair:** pick two CW20s with very shallow liquidity, enter a large swap amount → **Price impact** shows a high **worst-hop %** (not 0%), **Route spread check** card appears, and **Swap** is disabled with **Price impact too high for this trade** / insufficient-liquidity messaging when impact exceeds slippage. - [ ] **Slippage change:** with the same amount, open Settings and lower/raise slippage → quote refetches and the disabled state toggles consistently with tolerance. - [ ] **Multihop (client BFS):** if you have a 2+ hop CW20 route, confirm the same preflight card and impact line behave sensibly. - [ ] **Indexer route:** CW20→CW20 with indexer hybrid enabled (default path) still quotes and blocks correctly on extreme sizes. - [ ] **Forced on-chain failure (optional):** if you bypass UI with an old build or custom max_spread, confirm a \`Max spread assertion\` log surfaces as the **Trade rejected: price impact…** sentence (not the full wasm stack). - [ ] **Regression:** normal deep-liquidity swap still submits; direct single-hop pool swap still shows impact from pair \`simulation\` as before. --- @totdking — could you verify on your **CORAL → JADE** LocalTerra repro when you have a moment? Leaving this issue **open** until you sign off.
PlasticDigits commented 2026-05-06 14:45:22 +00:00 (Migrated from gitlab.com)

Formatting note: previous comment used escaped backticks; key links are docs/swap-max-spread-ux.md on main and commit 2683861.

Formatting note: previous comment used escaped backticks; key links are docs/swap-max-spread-ux.md on main and commit 2683861.
PlasticDigits commented 2026-05-06 14:48:19 +00:00 (Migrated from gitlab.com)

mentioned in commit c4bc1c2b55

mentioned in commit c4bc1c2b550dbc171be4528a8ba4f9c919db2421
PlasticDigits commented 2026-05-06 14:48:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #135

mentioned in issue #135
PlasticDigits commented 2026-05-06 14:48:57 +00:00 (Migrated from gitlab.com)

mentioned in commit 4bfcced796

mentioned in commit 4bfcced7963d6c52e05ca5f44a14d0c97dbd0c15
Brouie commented 2026-05-07 06:30:40 +00:00 (Migrated from gitlab.com)

@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:264

humanizeTerraTxError.ts:22-35 only matches Max spread assertion (this ticket) and "LimitOrder ... not found" (#135). Everything else falls through to:

return `Transaction failed: ${errorMessage}`;

Real-world contract error patterns that surface raw to retail users today:

  • Contract is paused — pair assert_not_paused rejection. e.g., during emergency pause; see DEX #120 thread.
  • Unauthorized — admin-gated entrypoint hit by non-admin sender
  • Insufficient funds — wallet doesn't have enough native LUNC for fees
  • out of gas — gas estimate undershoots actual usage. related to #115 / #127 history.
  • deadline exceeded — assert_deadline rejection on expired tx
  • InvariantViolation: pending escrow ... — pair contract invariant violations
  • generic wasm Std generic_err strings

Suggestion

either:

A. expand humanizeTerraTxError.ts here — add classifier branches for the patterns above, keep this ticket as the umbrella
B. 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.

TxResultAlert.tsx:14-16 renders {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 inside TxResultAlert itself, or a lint rule. that goes beyond this ticket scope but worth noting.

cc @PlasticDigits

@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:264` `humanizeTerraTxError.ts:22-35` only matches Max spread assertion (this ticket) and "LimitOrder ... not found" (#135). Everything else falls through to: ```ts return `Transaction failed: ${errorMessage}`; ``` Real-world contract error patterns that surface raw to retail users today: - `Contract is paused` — pair `assert_not_paused` rejection. e.g., during emergency pause; see DEX #120 thread. - `Unauthorized` — admin-gated entrypoint hit by non-admin sender - `Insufficient funds` — wallet doesn't have enough native LUNC for fees - `out of gas` — gas estimate undershoots actual usage. related to #115 / #127 history. - `deadline exceeded` — assert_deadline rejection on expired tx - `InvariantViolation: pending escrow ...` — pair contract invariant violations - generic `wasm Std generic_err` strings ## Suggestion either: **A. expand `humanizeTerraTxError.ts` here** — add classifier branches for the patterns above, keep this ticket as the umbrella **B. 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-16` renders `{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 inside `TxResultAlert` itself, or a lint rule. that goes beyond this ticket scope but worth noting. cc @PlasticDigits
Brouie commented 2026-05-07 06:38:59 +00:00 (Migrated from gitlab.com)

mentioned in issue #145

mentioned in issue #145
PlasticDigits commented 2026-05-07 12:11:17 +00:00 (Migrated from gitlab.com)

Expand humanizeTerraTxError is approved.
TxResultAlert add humanize step is approved & should be in a new issue.

Expand humanizeTerraTxError is approved. TxResultAlert add humanize step is approved & should be in a new issue.
Brouie commented 2026-05-08 02:59:32 +00:00 (Migrated from gitlab.com)

mentioned in commit 74e705a5ee

mentioned in commit 74e705a5ee578fde766281e929ece44f2dbc58b0
Brouie commented 2026-05-08 03:00:50 +00:00 (Migrated from gitlab.com)

@totdking — humanizer expansion shipped for verification, per dev's 5/07 sign-off on the audit note ("Expand humanizeTerraTxError is approved").

what changed

tryHumanizeTerraTxMessage in frontend-dapp/src/utils/humanizeTerraTxError.ts now classifies 6 additional raw chain-error patterns that previously bubbled through to the UI as raw dispatch dumps:

  • contract paused (assert_not_paused / generic "contract is paused") -> "This pool is currently paused by the operator. Try again later or pick a different pair."
  • Unauthorized (admin-gated entrypoint rejections) -> "You do not have permission for this action."
  • Insufficient funds (no LUNC for fees) -> "Insufficient LUNC for transaction fees. Top up your wallet and try again."
  • out of gas -> "Transaction needed more gas than estimated. Try again — gas usage can vary slightly between blocks."
  • assert_deadline / deadline exceeded -> "Transaction took too long to confirm and the deadline was reached. Try again."
  • InvariantViolation (pair contract invariant violations) -> "Pool state inconsistency detected. Refresh the page and try again. If this keeps happening, the pool may need operator attention."

each pattern uses a tight regex with case-insensitive matching. \bUnauthorized\b uses 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

  • new file: 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:")
  • updated: 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

  • branch: fix/glab-134-humanize-additional-chain-errors
  • commit: 74e705a

how to verify

  1. checkout the branch: git checkout fix/glab-134-humanize-additional-chain-errors
  2. cd frontend-dapp && npm install && npm run dev
  3. trigger the various error conditions on LocalTerra and confirm humanized messages appear:
    • out of gas: place a swap with extreme size or place a limit order with too-low gas (you can manually tweak gas in browser to test)
    • deadline exceeded: place a limit order with a very short deadline (1 second) and watch it expire before fill
    • insufficient funds: send all your LUNC away from the test wallet, attempt any tx — should see "Insufficient LUNC for transaction fees..."
    • paused / unauthorized / invariant: harder to trigger naturally; covered by the unit tests
  4. confirm existing humanized cases still work: max spread (the original #134 case) + duplicate cancel of a limit order (#135 humanizer)

verification gate (already passing on my side)

  • tsc -b clean
  • npm run test:unit — 334/334 PASS (44 files), up from 315 previously: +19 new humanizer tests
  • npm run lint — 0 errors. 3 pre-existing warnings on LimitOrdersPage.tsx (untouched)

scope notes

  • per dev's 5/07 note, the sibling carve-out for "TxResultAlert add humanize step" is approved to file separately. NOT included in this MR.
  • generic "wasm contract failed" cleanup (stripping dispatch chain noise from unrecognized errors) is OUT of scope here — would mask future unknown errors that should bubble for diagnostic purposes.

ping when verified, i'll open the MR after your sign-off.

@totdking — humanizer expansion shipped for verification, per dev's 5/07 sign-off on the audit note ("Expand humanizeTerraTxError is approved"). ## what changed `tryHumanizeTerraTxMessage` in `frontend-dapp/src/utils/humanizeTerraTxError.ts` now classifies 6 additional raw chain-error patterns that previously bubbled through to the UI as raw dispatch dumps: - **contract paused** (`assert_not_paused` / generic "contract is paused") -> "This pool is currently paused by the operator. Try again later or pick a different pair." - **Unauthorized** (admin-gated entrypoint rejections) -> "You do not have permission for this action." - **Insufficient funds** (no LUNC for fees) -> "Insufficient LUNC for transaction fees. Top up your wallet and try again." - **out of gas** -> "Transaction needed more gas than estimated. Try again — gas usage can vary slightly between blocks." - **assert_deadline / deadline exceeded** -> "Transaction took too long to confirm and the deadline was reached. Try again." - **InvariantViolation** (pair contract invariant violations) -> "Pool state inconsistency detected. Refresh the page and try again. If this keeps happening, the pool may need operator attention." each pattern uses a tight regex with case-insensitive matching. `\bUnauthorized\b` uses 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 - new file: `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:") - updated: `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 - branch: `fix/glab-134-humanize-additional-chain-errors` - commit: `74e705a` ## how to verify 1. checkout the branch: `git checkout fix/glab-134-humanize-additional-chain-errors` 2. `cd frontend-dapp && npm install && npm run dev` 3. trigger the various error conditions on LocalTerra and confirm humanized messages appear: - **out of gas**: place a swap with extreme size or place a limit order with too-low gas (you can manually tweak gas in browser to test) - **deadline exceeded**: place a limit order with a very short deadline (1 second) and watch it expire before fill - **insufficient funds**: send all your LUNC away from the test wallet, attempt any tx — should see "Insufficient LUNC for transaction fees..." - **paused / unauthorized / invariant**: harder to trigger naturally; covered by the unit tests 4. confirm existing humanized cases still work: max spread (the original #134 case) + duplicate cancel of a limit order (#135 humanizer) ## verification gate (already passing on my side) - `tsc -b` clean - `npm run test:unit` — 334/334 PASS (44 files), up from 315 previously: +19 new humanizer tests - `npm run lint` — 0 errors. 3 pre-existing warnings on `LimitOrdersPage.tsx` (untouched) ## scope notes - per dev's 5/07 note, the sibling carve-out for "TxResultAlert add humanize step" is approved to file separately. NOT included in this MR. - generic "wasm contract failed" cleanup (stripping dispatch chain noise from unrecognized errors) is OUT of scope here — would mask future unknown errors that should bubble for diagnostic purposes. ping when verified, i'll open the MR after your sign-off.
PlasticDigits commented 2026-05-09 09:54:12 +00:00 (Migrated from gitlab.com)

mentioned in commit ecdc2723c9

mentioned in commit ecdc2723c99a9ee1fd7a1a5b7de333a110da434d
PlasticDigits commented 2026-05-09 10:03:12 +00:00 (Migrated from gitlab.com)

Merged, ready for verification @totdking

Merged, ready for verification @totdking
totdking commented 2026-05-22 13:40:03 +00:00 (Migrated from gitlab.com)

Verification checklist

  • LocalTerra thin pair: pick two CW20s with very shallow liquidity, enter a large swap amount → Price impact shows a high worst-hop % (not 0%), Route spread check card appears, and Swap is disabled with Price impact too high for this trade / insufficient-liquidity messaging when impact exceeds slippage.
  • Slippage change: with the same amount, open Settings and lower/raise slippage → quote refetches and the disabled state toggles consistently with tolerance.
  • Multihop (client BFS): if you have a 2+ hop CW20 route, confirm the same preflight card and impact line behave sensibly.
  • Indexer route: CW20→CW20 with indexer hybrid enabled (default path) still quotes and blocks correctly on extreme sizes.
  • Forced on-chain failure (optional): if you bypass UI with an old build or custom max_spread, confirm a `Max spread assertion` log surfaces as the Trade rejected: price impact… sentence (not the full wasm stack).
  • Regression: normal deep-liquidity swap still submits; direct single-hop pool swap still shows impact from pair `simulation` as before.

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

### Verification checklist * [x] **LocalTerra thin pair:** pick two CW20s with very shallow liquidity, enter a large swap amount → **Price impact** shows a high **worst-hop %** (not 0%), **Route spread check** card appears, and **Swap** is disabled with **Price impact too high for this trade** / insufficient-liquidity messaging when impact exceeds slippage. * [x] **Slippage change:** with the same amount, open Settings and lower/raise slippage → quote refetches and the disabled state toggles consistently with tolerance. * [x] **Multihop (client BFS):** if you have a 2+ hop CW20 route, confirm the same preflight card and impact line behave sensibly. * [x] **Indexer route:** CW20→CW20 with indexer hybrid enabled (default path) still quotes and blocks correctly on extreme sizes. * [ ] **Forced on-chain failure (optional):** if you bypass UI with an old build or custom max_spread, confirm a \`Max spread assertion\` log surfaces as the **Trade rejected: price impact…** sentence (not the full wasm stack). * [x] **Regression:** normal deep-liquidity swap still submits; direct single-hop pool swap still shows impact from pair \`simulation\` as before. ## 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
PlasticDigits commented 2026-05-25 02:09:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 6c148e8252

mentioned in commit 6c148e82527054c909ca6ae013450ac5075ec2d5
PlasticDigits commented 2026-05-25 02:09:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 19766b9cd7

mentioned in commit 19766b9cd73e4ab3c67f0d190bd43f8edeec1f2c
PlasticDigits commented 2026-05-25 02:09:58 +00:00 (Migrated from gitlab.com)

Update (merged to main)

Follow-up for @totdking’s remaining checklist items on #134:

Commit: 6c148e8 on main.

What changed

  1. Forced on-chain Max spread → retail copy

    • terraBroadcast.ts now throws humanized Trade rejected: price impact… directly (no double Transaction failed: prefix).
    • TxResultAlert + humanizeUserFacingError regression tests cover the full wasm log shape.
  2. Wallet fee ~36 → ~23 LUNC → out of gas

    • Pool-only CW20 send→swap (and top-level swap) gas now uses the same 830k buffered envelope as one-hop execute_swap_operations (was 600k, below #115 observed ~753k usage).
    • LocalTerra post-sign guard tightened from 50% to 95% of dApp fee amount and gas (extensionSignedFeeGuard.ts + cosmes KeplrExtension patch) so Station cannot halve the signed envelope and still pass.
    • packages/localnet-trading-swarm/src/gas.ts kept in sync.

Docs / agents: docs/swap-max-spread-ux.md · docs/frontend.md § Swap · skills/AGENTS_TERRACLASSIC_GAS.md · skills/AGENTS_LOCALNET_TRADING_SWARM.md

Tests: npm run test:unit — 542/542 PASS.


Verification checklist

  • Forced on-chain failure (optional): bypass UI preflight (old build or custom max_spread) → alert shows Trade rejected: price impact… (not the wasm dispatch stack).
  • Swap fee stability: CORAL→JADE (or any pool-only CW20 swap) — wallet fee stays ~23–36 LUNC band consistent with 830k gas; tx succeeds. If Station rewrites fee/gas below 95%, dApp blocks broadcast with Wallet signed a fee far below… (reconnect + npm ci).
  • Regression: thin-pair preflight still disables submit; deep-liquidity swap still succeeds; multihop/indexer paths unchanged.

@brouie — please verify the two items above on LocalTerra when you can. Leaving #134 open for @totdking sign-off.

## Update (merged to `main`) Follow-up for @totdking’s remaining checklist items on [**#134**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/134): **Commit:** `6c148e8` on `main`. ### What changed 1. **Forced on-chain Max spread → retail copy** - `terraBroadcast.ts` now throws humanized `Trade rejected: price impact…` directly (no double `Transaction failed:` prefix). - `TxResultAlert` + `humanizeUserFacingError` regression tests cover the full wasm log shape. 2. **Wallet fee ~36 → ~23 LUNC → `out of gas`** - Pool-only CW20 `send`→`swap` (and top-level `swap`) gas now uses the same **830k** buffered envelope as one-hop `execute_swap_operations` (was **600k**, below #115 observed ~753k usage). - LocalTerra post-sign guard tightened from **50%** to **95%** of dApp **fee amount and gas** (`extensionSignedFeeGuard.ts` + cosmes `KeplrExtension` patch) so Station cannot halve the signed envelope and still pass. - `packages/localnet-trading-swarm/src/gas.ts` kept in sync. **Docs / agents:** [`docs/swap-max-spread-ux.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/swap-max-spread-ux.md) · [`docs/frontend.md` § Swap](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/frontend.md) · [`skills/AGENTS_TERRACLASSIC_GAS.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_TERRACLASSIC_GAS.md) · [`skills/AGENTS_LOCALNET_TRADING_SWARM.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_LOCALNET_TRADING_SWARM.md) **Tests:** `npm run test:unit` — **542/542** PASS. --- ### Verification checklist - [ ] **Forced on-chain failure (optional):** bypass UI preflight (old build or custom `max_spread`) → alert shows **Trade rejected: price impact…** (not the wasm dispatch stack). - [ ] **Swap fee stability:** CORAL→JADE (or any pool-only CW20 swap) — wallet fee stays ~**23–36 LUNC** band consistent with **830k** gas; tx succeeds. If Station rewrites fee/gas below 95%, dApp blocks broadcast with **Wallet signed a fee far below…** (reconnect + `npm ci`). - [ ] **Regression:** thin-pair preflight still disables submit; deep-liquidity swap still succeeds; multihop/indexer paths unchanged. @brouie — please verify the two items above on LocalTerra when you can. Leaving **#134** open for @totdking sign-off.
PlasticDigits commented 2026-05-25 02:10:06 +00:00 (Migrated from gitlab.com)

mentioned in issue #138

mentioned in issue #138
totdking commented 2026-05-26 11:00:11 +00:00 (Migrated from gitlab.com)

Verification checklist

  • Forced on-chain failure (optional): bypass UI preflight (old build or custom max_spread) → alert shows Trade rejected: price impact… (not the wasm dispatch stack).
  • Swap fee stability: CORAL→JADE (or any pool-only CW20 swap) — wallet fee stays ~23–36 LUNC band consistent with 830k gas; tx succeeds. If Station rewrites fee/gas below 95%, dApp blocks broadcast with Wallet signed a fee far below… (reconnect + npm ci).
  • Regression: thin-pair preflight still disables submit; deep-liquidity swap still succeeds; multihop/indexer paths unchanged.

Good to close on this end .

cc: @PlasticDigits

### Verification checklist * [x] **Forced on-chain failure (optional):** bypass UI preflight (old build or custom `max_spread`) → alert shows **Trade rejected: price impact…** (not the wasm dispatch stack). * [x] **Swap fee stability:** CORAL→JADE (or any pool-only CW20 swap) — wallet fee stays \~**23–36 LUNC** band consistent with **830k** gas; tx succeeds. If Station rewrites fee/gas below 95%, dApp blocks broadcast with **Wallet signed a fee far below…** (reconnect + `npm ci`). * [x] **Regression:** thin-pair preflight still disables submit; deep-liquidity swap still succeeds; multihop/indexer paths unchanged. Good to close on this end . cc: @PlasticDigits
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-26 11:23:13 +00:00
PlasticDigits commented 2026-06-25 14:12:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #412

mentioned in issue #412
PlasticDigits commented 2026-06-25 14:12:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #414

mentioned in issue #414
totdking commented 2026-06-29 14:50:34 +00:00 (Migrated from gitlab.com)

mentioned in issue #429

mentioned in issue #429
totdking commented 2026-06-30 18:31:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #460

mentioned in issue #460
PlasticDigits commented 2026-07-12 07:09:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #475

mentioned in issue #475
PlasticDigits commented 2026-08-22 11:02:34 +00:00 (Migrated from gitlab.com)

mentioned in issue #595

mentioned in issue #595
PlasticDigits commented 2026-08-27 00:17:52 +00:00 (Migrated from gitlab.com)

mentioned in issue #678

mentioned in issue #678
PlasticDigits commented 2026-08-27 00:17:55 +00:00 (Migrated from gitlab.com)

marked as related to #678

marked as related to #678
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#134
No description provided.