Native-wrap swap path always out of gas: router-mediated single hop priced as direct-to-pair (1.14M limit vs ~1.59M actual) #353
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#353
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
Swapping native LUNC to any CW20 through the auto-wrap path (LUNC -> LUNC-C -> token) fails deterministically with the humanized OOG message ("Transaction needed more gas than estimated. Try again..."). The broadcast gas limit is a fixed 1,140,000 while the transaction actually needs ~1.59M — every attempt lands code 11, and retrying can't help because the limit is a constant, not a simulation.
Evidence (LocalTerra, main
83dc192)A125F7C8C09A8CB20DFCD32F79586720F2239B10AB433CDCD81243C3D9082AAF: code 11, gasWanted 1,140,000, aborted at gasUsed 1,187,454 ("out of gas in location: wasm contract").wrap_deposit(native funds) to the wrap mapper; msg2 = LUNC-C cw20send-> routerexecute_swap_operationswith ONEterra_swapop (LUNC-C -> EMBER). The send amount is the post-tax figure (99,502,487 = 100M x 0.995), so the #342 fix is composing the message correctly — this is purely a gas-budget failure.wrap_depositalone: gasUsed 301,061 — already overWRAP_GAS_LIMIT = 300000on its own.execute_swap_operations, single hop, pool-only: gasUsed 1,283,903.Root cause (source layer)
getGasLimitForTxprices the two messages independently:wrap_deposit->WRAP_GAS_LIMIT(300,000), and the swap msg -> the single-hop budget (840,000 viagasLimitForExecuteSwapOperations(1)), totalling 1,140,000.The 840k single-hop budget was calibrated for direct-to-pair swaps — #249 deliberately routes
ops.length == 1straight to the pair contract. The wrap path is the exception: it always sends its single op through the router (router execution + reply handling + the extra CW20 hop), which measures ~1.28M and is never priced by the model.WRAP_GAS_LIMITis also a hair under the realwrap_depositcost.Fix direction
WRAP_GAS_LIMITabove the measured 301,061 (e.g. 400k).Impact
Native LUNC is the primary retail entry on Terra Classic, and this blocks every native -> CW20 swap end to end. Found running SW-00-03 of #337 — that row stays FAIL until this lands. Cluster sibling of the add-liquidity OOG (fixed), create-pair fee+gas (#345, fixed), and the CW20 -> native unwrap fee estimate (#343, fixed): this is the fourth member, the wrap direction on swap.
cc @PlasticDigits
Two additions from continuing the browser pass:
Not wrap-specific — plain CW20->CW20 multi-hop has the same failure. EMBER->JADE->RUBY (2 hops, Expert Mode on, hop spread 3.25% so all guards pass): five identical broadcasts, every one code 11 with gasWanted 1,670,000 vs gasUsed ~1,717,973 (e.g. tx 24517C6A9B9C3EEEEAC4F...). So the 2-hop router budget is ~3% under actual too. Practical effect: #341's preflight fix works (multi-hop now reaches broadcast) but multi-hop execution still fails end to end — on gas instead. Worth treating both calibrations in one pass.
The dapp already disagrees with itself about this cost. The Max-button fee reserve (estimateNativeSwapUlunaFeesTotal) prices the same native wrap-swap at 1,970,000 gas (reserve 55.80 LUNC — verified to the micro-unit against the Max fill), while the broadcast path (getGasLimitForTx summing WRAP_GAS_LIMIT + single-hop) only grants 1,140,000. The reserve-side envelope is the safe one; aligning broadcast limits with it (or with fresh measurements) closes the gap.
mentioned in issue #337
mentioned in commit
4eaeba6d12MR !854. Router 1-hop 1.4M, 2-hop floor 900k/hop, WRAP 400k. Verify: LUNC→CW20 wrap-swap and 2-hop CW20 swap broadcast succeed on LocalTerra.
mentioned in merge request !854
mentioned in commit
aa15ec1734Verified the merged fix (!854, commit
4eaeba6) at source + unit-test layer one198dcb. Router/wrap-swap legs now price through gasLimitForRouterExecuteSwapOperations: single-hop 1.4M, multi-hop floored at 900k/hop (2-hop 1.81M), and WRAP_GAS_LIMIT 300k->400k; direct-to-pair swap stays at 840k by design. These clear the on-chain actuals I measured (wrap_deposit 301k, router single-hop 1.28M, 2-hop 1.72M), and estimate + broadcast share the same path so they stay aligned. transactions.test 51/0 plus feeEstimate/hybridSwapGas/batchCancel 14/0; the new assertions encode the observed OOG floor (~1,718,000). Live dapp broadcast of a native wrap-swap and a 2-hop swap is the laptop re-test, in progress.mentioned in issue #357
The 2-hop half is still short — fresh live evidence at
3169af0, dapp + Simulated Wallet, EMBER -> JADE -> RUBY (no direct pair), 0.1 EMBER, router execute_swap_operations.Three attempts, all code 11 against the recalibrated 1,810,000 two-hop budget:
Real cost of this route is ~1.8101M — the budget misses by 3-138 gas across blocks, so the "try again" path can never save it. Why it runs heavier than the ~1.72M actuals the recalibration was measured against: the receiver had never held RUBY, so the final hop pays the cold-balance storage write that warm-wallet calibration runs never see. Same class as the add-liq (!843) and create-pair (#345) findings — fixed constants vs route/token variance.
The wrap half IS fixed, verified live: native LUNC -> EMBER wrap-swap tx 28EA7D0F, code 0, gasUsed 1,534,413 vs the new 1,800,000 budget — the old 1.14M would have OOG'd it. That part holds.
Suggestion: bump the per-hop floor 900k -> 950k (2hop -> 1.9M; today's heaviest clears with ~5% headroom), or move router paths to simulated gas instead of constants — third constant-related gas bug in this cluster. SW-00-03 on #337 stays FAIL on the 2-hop leg meanwhile. @PlasticDigits
mentioned in merge request !869
Reopen evidence — the 2-hop half of this is still broken at main
8408689. The wrap fix landed, but the multi-hop router gas floor was never raised, so 2-hop swaps still OOG deterministically.Live repro from the dapp (EMBER->JADE->RUBY, Expert Mode, on-chain):
Same knife-edge I flagged on 06-11:
gasLimitForRouterExecuteSwapOperations(2)= ROUTER_SWAP_OPS_MIN_GAS_PER_HOP (900k) x2 + 10k safety = exactly 1,810,000, which is precisely what the swap needs — no headroom, tips over every block. The 2-hop test only guarded > 1,718,000 (stale), so it never caught it.Fix up in !869: floor -> 950k/hop (2-hop 1,910,000, ~100k headroom over the worst observed 1,810,206), single-hop and direct paths untouched, regression guard re-floored above the live number. transactions.test.ts 51/51.
@PlasticDigits this needs reopening — SW-00-03 2-hop on #337 is still a FAIL until !869 (or an equivalent bump) merges. The wrap half you fixed is good; it's just the 2-hop floor that got missed.
mentioned in commit
c492682376mentioned in merge request !952
mentioned in commit
767dcd1e1dmentioned in issue #587
marked as related to #587
mentioned in merge request !1113
mentioned in issue #599
marked as related to #599
mentioned in issue #679
marked as related to #679
mentioned in issue #681
marked as related to #681