bug: USTR→USTC unwrap+2hop out of gas after #587 wrap fix #599
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#599
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
Retail report (2026-08-23, columbus-5,
dex.cl8y.com/): USTR → USTC Swap quotes and signs, then fails with the humanized out-of-gas toast (“Transaction needed more gas than estimated. Try again — gas usage can vary slightly between blocks.”). Retrying does not help. The same bake succeeds for LUNC → USTR and USTC → USTR.Screenshot chrome confirms the native unwrap path: Wrap limit 1.1M / 1.1M USTC available, Worst hop spread ≈ 0.00%, SWAP CTA, MAINNET / columbus-5. Quote succeeded; execute OOGed.
Do not reopen #587. That ticket shipped wrap+≥2hop combo overhead (
WRAP_ROUTER_COMBO_OVERHEAD_GAS400k) and Network-fee chrome. This is the remaining CW20 → nativeuusdhole: same 2-hop hub graph,unwrap_output, nowrap_deposit, and InstantWithdraw of taxed USTC. Bundle measurement, envelope raise, inventory/E2E, and docs into one issue.Related: #587 (closed: wrap+≥2hop LUNC↔USTR), #343 (
unwrap_outputadd-on), #353 (router single-hop floor), #475 (retail gas inventory), #590 (E7/E8 Playwright still chain-gated), #596 / H596-7 (native path is not Pattern C hybrid).Current codebase
USTR is CW20
terra1vy3kc0…fsetxv. Native USTC isuusd; wrap CW20 is cUSTCterra1nap4dx…cr56ch. There is no factory pair USTR/cUSTC. Columbus-5 hub graph (indexerGET /api/v1/pairs, 2026-08-23):terra1cepr…w55fterra16vxr…5hgqyterra1su53…mm7h4Client BFS therefore routes USTR → USTC as USTR → UST1 → cUSTC (2 hops) + router
unwrap_output: true.SwapPagenativeRouteInfobranch callssimulateNativeSwap(walletsimulate_swap_operations+ unwrap fee/tax net). Does not call indexerGET /route/solve. Hops are pool-only.isWrapOrUnwrap || nativeRouteInfo→executeNativeSwap. Comment on the hook: “Pool-only hops from client BFSfindRoute— never copy hybrid / book_input (#587).” Single CW20send→ routerexecute_swap_operationswithunwrap_output: true. Nowrap_deposit.getGasLimitForTxon that send:gasLimitForRouterExecuteSwapOperations(2) 1,910,000 +UNWRAP_GAS_LIMIT800k = 2,710,000 (~76.76 LUNC at 28.325).WRAP_ROUTER_COMBO_OVERHEAD_GAS(400k) applies only when awrap_depositand router send (N≥2) share one multi-msg tx. USTR→USTC has no wrap msg, so combo overhead is 0. Wrap+2hop+unwrap is documented at 3,510,000; unwrap-only 2-hop stays 2,710,000.{ unwrap }(~562k used; failed tx wanted 550k). That is not routerunwrap_outputafter two hub hops, and not treasuryInstantWithdrawofuusd(Classic burn tax / extra bank sends).send_2hop_unwrapis labeled USTR→LUNC (uluna unwrap). Playwrightwrap-swap.spec.tsE8 is CW20 → LUNC, not → USTC. No combined envelope named for hub USTR→USTC.uusdunwrap and was never measured.GET /route/solve?token_in=USTR&token_out=cUSTC(2026-08-23) returns global_v4, 2 hops, 100%book_inputon both USTR/UST1 and UST1/cUSTC (max_maker_fills: 8). Retail Swap does not submit those ops when the receive token is nativeuusd(H596-7). The OOG toast isgasUsed > gasWantedon the pool-only + unwrap envelope, not a hybrid book walk. Do not “fix” this by copying solverhybridontoexecuteNativeSwap.humanizeTerraTxErrormaps/out of gas/ito the screenshot string. Retrying the same 2.71M limit cannot succeed (#353 / #587).WrapRateLimitStatuson nativeuusdis expected (#502 / #507). Fully available 1.1M/1.1M is not a rate-limit block; unwrap does not consume the wrap window.Why this is needed
USTR → native USTC is a primary off-ramp (CW20 hub → taxed
uusd). After #587, wrap-direction hub swaps work; this reverse still burns the failed-tx fee and leaves traders stuck. “Try again” is false — the constant is too low. #587’s unwrap fixture and E8 cover LUNC, so USTC InstantWithdraw + 2 hub hops never had a measured ceiling.Constraints / guardrails
executeNativeSwap(BFS +unwrap_output). Do not tell users to hop USTR→UST1→cUSTC→unwrap by hand.hybrid/book_input(would jump toward 15M per hop, #249 / #260 / H596-7). IndexerGET /route/solvefor nativeuusdstays out of scope.uluna. Do not add USTC / multi-denom fees.AGENTS_TERRACLASSIC_GAS). If LCD sim is used for tuning, new constants must still exceed measuredgasUsedwith wasm/storage headroom.gasUsedis known, new defaults must exceed it with margin (Classic variance is not tight). Same rule as #353 / #587.UNWRAP_GAS_LIMITblindly for every unwrap if the miss is router+unwrap combo (same-msg overhead after N≥2 hops, analog ofWRAP_ROUTER_COMBO_OVERHEAD_GAS). Prefer a named combo / router-unwrap add-on so direct cUSTC→USTC unwrap (800k) does not pay hub-multihop tax.packages/localnet-trading-swarm/src/gas.ts.RETAIL_COMBINED_ENVELOPE_FIXTURESfor USTR→USTC (2hop +unwrap_output, no wrap). Do not fall through toSWAP_GAS_LIMIT600k (SendHookGasDecodeError).estimateNativeSwapUlunaFeesTotal/swap-network-fee/ submit uluna gate must use the same envelope as broadcast (including the new unwrap combo).minimum_receiveon unwrap = post-fee pre-tax (R3). Display You Receive stays post–burn-tax (#512). Do not retune wrap-mapper bps here.route/solveforuusd. Station-on-LocalTerra (#235). Changing wrap-limit chrome (informational only).Relevant files
frontend-dapp/src/utils/constants.tsUNWRAP_GAS_LIMIT,WRAP_ROUTER_COMBO_OVERHEAD_GAS,ROUTER_*frontend-dapp/src/services/terraclassic/terraGas.tsgasLimitForSwapOperationsMsg(+unwrap),wrapRouterComboOverheadGas,totalGasLimitForExecuteMsgsfrontend-dapp/src/services/terraclassic/transactions.tsnativeSwapFeeExecuteMsgs/estimateNativeSwapUlunaFeesTotalfrontend-dapp/src/services/terraclassic/router.tsfindRouteWithNativeSupport,executeNativeSwap,simulateNativeSwapfrontend-dapp/src/pages/SwapPage.tsxfrontend-dapp/src/services/terraclassic/terraGasRetailInventory.tsfrontend-dapp/src/services/terraclassic/__tests__/terraGas.retailShapes.test.tsfrontend-dapp/src/services/terraclassic/__tests__/transactions.test.tsfrontend-dapp/src/services/terraclassic/__tests__/swapNetworkFee.test.tsfrontend-dapp/e2e/wrap-swap.spec.tsuusdunwrap stand-in)packages/localnet-trading-swarm/src/gas.tsdocs/frontend.mdskills/AGENTS_TERRACLASSIC_GAS.mdAGENTS.mdmake verify-issue-<iid>Recommended direction
gasWanted/gasUsed) from the reporter. Else LCD-sim and/or LocalTerra combined single-msg: 2-hop router +unwrap_outputintouusd(not wrap-alone + unwrap-alone). Also compare USTR→LUNC (uluna unwrap, same 2.71M) so we know if the miss is USTC-tax-specific or all unwrap+2hop.unwrap_output. Raise whichever is short:unwrap_outputadd-on above mapper-onlyUNWRAP_GAS_LIMIT, and/orgas_usedalone exceeds 1.91M.send_2hop_unwrap_ustc. Playwright: USTR→USTC one submit; assert no OOG toast;gas_used < gas_wanted. Keep E7/E8 LUNC paths.docs/frontend.md+AGENTS_TERRACLASSIC_GAS.md+make verify-issue-<iid>.Acceptance criteria
gasWantedexceeds measuredgasUsedwith documented margin. Combined unwrap+2hop envelope is in inventory and matchestotalGasLimitForExecuteMsgs.UNWRAP_GAS_LIMIT(800k) unless measurement proves it must rise; do not silently charge hub-multihop combo on a 1:1 unwrap.executeNativeSwaphops remain pool-only (nohybrid/book_inputfrom indexer or query string).send.msgstill throwsSendHookGasDecodeError.make verify-issue-<iid>+ docs/skill row. Playwright USTR→USTC (or documented stand-in) inwrap-swap.spec.ts.Test plan (all paths)
uusd)uluna)simulateNativeSwapstill pool-only; receive is post-unwrap-fee post-taxLocalTerra: prefer hub USTR/UST1/cUSTC if seeded; else shortest ≥2hop CW20 → native
uusdunwrap and document the stand-in. Cloud Agent:make setup-cloud-localterra(not Postgres-only).Test plan (attack, hack, abuse)
executeNativeSwapmust not accept attackerhybrid/book_inputfrom query string, indexerrouter_operations, or typed Advanced book whentoTokenisuusd. Ops from client BFS only.hopCountfor Max/fee must equal submittedoperations.length. A crafted shorter hint must not produce a smallerFee.gasthan execute.send.msgtamper / decode fail — refuse undersized 600k fallback (SendHookGasDecodeError).gas_wanted(#429). Do not treat that as “raise to 15M.”Fee.amount. Prefer blocked CTA over OOG after sign.max_spread/minimum_receive(R3 post-fee pre-tax). OOG fix is not a license to widen default 5% slippage.Fee.amountin LUNC. Dust USTR→USTC still pays the full unwrap+2hop limit (document; no hidden min that looks like a pause).LUNC; no raw LCDerror.messageinterpolation.Verification criteria
Manual on production
/: USTR→USTC shows RouteUSTR → UST1 → cUSTC → USTC(or equivalent), Network fee ~N LUNC, one Swap, success, no OOG toast. LUNC→USTR and USTC→USTR still one-tx success.Done when ACs 1–10 pass, verify script is green, and unwrap+2hop / USTC InstantWithdraw are in
docs/frontend.md+AGENTS_TERRACLASSIC_GAS.md.marked as related to #587
marked as related to #343
marked as related to #353
marked as related to #590
marked as related to #596
mentioned in issue #587
mentioned in issue #590
mentioned in commit
9c2035e21ementioned in merge request !1118
mentioned in commit
328b2b8d6dmentioned in commit
4f245fc839mentioned in issue #595
Merge review (!1118 →
main)Merged as https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/400 (
4f245fc8) after resolving conflicts with !1117 (#595). Conflict files were docs/Makefile/AGENTS only; gas inventory auto-merged and keeps bothsend_2hop_unwrap_ustcandwrap_plus_2hop_plus_invoice_send. GitLab CI failed withci_quota_exceeded(not a test failure). Local sanity on the combined tree: 279 Vitest cases green.Acceptance vs this MR
UNWRAP_ROUTER_COMBO_OVERHEAD_GAS400k)send_2hop_unwrap)executeNativeSwapstays pool-onlybook_input)send.msgstill throwsSendHookGasDecodeErrorgasUsedhash was attached; 400k is the wrap-combo analogUnit/docs ACs are on
main. Chain/manual ACs are still open — follow-up issue filed after this comment.mentioned in issue #600
marked as related to #600
mentioned in issue #679
mentioned in issue #681
mentioned in issue #690