Three-tx - provide_liquidity() sequence burns two allowance gas fees when final tx fails; no pre-flight LUNC check #147
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#147
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
Providing liquidity with two CW20 tokens sends three sequential transactions:
increase_allowancefor token A (~5.665 LUNC gas),increase_allowancefor token B (~5.665 LUNC gas), thenprovide_liquidity(~variable LUNC gas). No pre-flight check validates that the wallet holds enough LUNC to cover all three fees before the sequence begins. If theprovide_liquiditytx fails — due to insufficient LUNC, user rejection, or on-chain error — both allowance fees (~11.33 LUNC combined) are already consumed and no liquidity is added. This is the same structural gap as issue #132 but affects the Pool page and involves three transactions instead of two.Reproduction Steps
/pooland select a CW20/CW20 pairincrease_allowancetoken A, ~5.665 LUNC fee)increase_allowancetoken B, ~5.665 LUNC fee)provide_liquidity) fails or is rejected due to insufficient LUNCExpected Behavior
Before broadcasting the first
increase_allowancetransaction, the frontend should validate that the wallet's LUNC balance is sufficient to cover all three estimated fees combined. If not, the Add Liquidity button should be disabled with a clear message such as: "Not enough LUNC for gas. You need at least ~X LUNC to cover all three transactions (allowance A + allowance B + provide liquidity)."Actual Behavior
The three-tx sequence in
pair.ts:provideLiquiditystarts immediately without any LUNC pre-flight check:executeTerraContract→increase_allowancetoken A — succeeds, ~5.665 LUNC consumedexecuteTerraContract→increase_allowancetoken B — succeeds, ~5.665 LUNC consumedexecuteTerraContract→provide_liquidity— fails (insufficient LUNC or rejected), LUNC goneThe catch block in
pair.ts:179attemptsdecrease_allowancecleanup on both tokens, which would trigger two additional Keplr prompts and burn even more gas; compounding the loss further if the user approves them.Screenshot
1st increase_allowance()
Second increase_allowance()
The actual tx carried out
Post balance:
Root Cause
frontend-dapp/src/services/terraclassic/pair.ts:152–195—provideLiquidity()calls three sequentialexecuteTerraContract()calls with no total LUNC balance check before starting. The wallet's native LUNC balance is not fetched or compared against the summed fee estimate (fee_allowanceA + fee_allowanceB + fee_provide) at any point before the first tx is broadcast.Note: the native/wrap path in
PoolPage.tsx:256avoids this by usingexecuteTerraContractMulti(single Keplr prompt, one tx fee), but the standard CW20 path routes throughpair.ts:provideLiquidityand is fully exposed.Relevant files:
frontend-dapp/src/services/terraclassic/pair.ts:152— three-tx sequence with no pre-flight LUNC checkfrontend-dapp/src/pages/PoolPage.tsx:259— callsprovideLiquidity()for the non-native pathEnvironment Details
localterra(local Docker)VITE_NETWORK=local npm run devmake indexer-dev)make deploy-localWallet / Device Details
http://localhost:26657Severity / Impact
P2 Polish. If the wallet holds sufficient LUNC to cover all three fees, the sequence completes normally and liquidity is added without issue. The problem only surfaces when LUNC is insufficient in that case, both allowance fees (~11.33 LUNC combined) are already consumed before the failure, with no liquidity added. The failure is fully preventable client-side , the total fee can be estimated and compared against the native LUNC balance before the first tx is sent, mirroring the fix needed in issue https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/132 . The cleanup
decrease_allowancepath in the catch block would add further gas loss if triggered. Related to issue https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/132 (same root pattern on limit orders).cc: @PlasticDigits
assigned to @Brouie
mentioned in issue #116
mentioned in commit
4f693144a4Ship note — native LUNC preflight for CW20/CW20 add liquidity (merged to
main)Implemented GitLab #147: before the first
increase_allowanceon the CW20/CW20 path, the Pool page now requires bank uluna ≥estimateProvideLiquidityCw20SequenceUlunaFeesTotal()(twoBASE_GAS_LIMITallowance txs +ADD_LIQUIDITY_GAS_LIMITforprovide_liquidity), matchingFee.amountmath intransactions.ts. The gate is skipped when either asset uses Use native (auto-wrap) (executeTerraContractMulti— single combined fee).Code / docs
estimateProvideLiquidityCw20SequenceUlunaFeesTotal, UI gateprovideLiquidityNativeGasBalanceGate.ts, wired inPoolPage.tsx(+ mutation guard + tests).docs/frontend.md(gas limits table + Pool section), crosslink indocs/limit-orders.md, agent playbookskills/AGENTS_TERRACLASSIC_GAS.md.Verification checklist (for QA / sign-off)
cc @totdking — could you confirm on your setup when you have a cycle?
Issue left open until verified.
mentioned in commit
36be564d53Follow-up:
decrease_allowancerollback (merged)Previously, when
provide_liquidityfailed after both allowances succeeded, the catch path fired two separateexecuteTerraContractcalls — two Keplr prompts and two fees if the user approved cleanup.Change: rollback now uses
executeTerraContractMultiwith both CW20decrease_allowancemessages in one transaction (pair.ts): one prompt / one native fee.getGasLimitForTxtreatsdecrease_allowancelikeincrease_allowance(BASE_GAS_LIMIT).Docs crosslinked:
docs/frontend.md§ Pool page,docs/limit-orders.md,skills/AGENTS_TERRACLASSIC_GAS.md.Manual check: force
provide_liquidityto fail after allowances (e.g. reject/spend path) — cleanup should be one Keplr approval for two contract executes in one tx.added 1 design
walked the native LUNC preflight fix at HEAD
94adb5f.Source
frontend-dapp/src/services/terraclassic/transactions.ts:156—estimateProvideLiquidityCw20SequenceUlunaFeesTotal()returns2 * allowanceFee + provideFee, derived fromgetGasLimitForTxper message shape (BASE_GAS_LIMITfor allowance,ADD_LIQUIDITY_GAS_LIMITfor provide). Math matches the ship-note formula.frontend-dapp/src/utils/provideLiquidityNativeGasBalanceGate.ts—evaluateProvideLiquidityCw20NativeGasGate()returnscanAddLiquidity: false+ explanatoryuserMessagewhen bank uluna < required.frontend-dapp/src/pages/PoolPage.tsx:194-197— gate is short-circuited tocanAddLiquidity: truewhenneedsWrapA || needsWrapBis true (native auto-wrap path uses single-promptexecuteTerraContractMultiand pays only one fee, so the three-fee floor doesn't apply). Confirms item 3 of your checklist.frontend-dapp/src/services/terraclassic/pair.ts:191-199— rollback path onprovide_liquidityfailure now usesexecuteTerraContractMultiwith bothdecrease_allowancemessages in a single tx (one Keplr prompt / one fee). Confirms the follow-up ship note.Tests at HEAD
94adb5ftransactions.test.ts(estimate + gas-limit selection): all PASSprovideLiquidityNativeGasBalanceGate.test.ts(gate util): all PASSPoolPage.test.tsx: 7/7 PASS, including the labeled integration testdisables provide when LUNC is below the three-tx CW20 path gas floor (GitLab #147)Checklist walk
(1) CW20/CW20 pair, wallet with enough CW20 but LUNC below the three-fee floor — Provide Liquidity stays disabled with alert.
(2) Same scenario with enough LUNC — three sequential txs complete, liquidity added.
canAddLiquidity: truewhen balance ≥ required.(3) Native auto-wrap on one or both sides — no erroneous "three fee" block when only one fee applies.
needsWrapA || needsWrapBis true.(4) Station + Terra Classic Keplr fee parity on LocalTerra (per #127).
(5) Rollback path: force
provide_liquidityto fail after allowances; cleanup should be one Keplr prompt for two contract executes in one tx.executeTerraContractMultiwith bothdecrease_allowancemessages bundled.Where I'm at
Source + automated tests cover items 1, 2, 3, 5 by structure. Items 1, 2, 5 are exercised by the existing component + util tests; item 3 is short-circuit logic that's effectively pure. Item 4 is the wallet-parity smoke test which needs a real Station + Keplr walk.
The four remaining UI walks all depend on the laptop tunnel stabilizing — same blocker as the #120 manual walk earlier today. Will revisit them in batch in the next session.
ready for partial close: source + automated coverage clean, browser walks pending.
/cc @PlasticDigits
@Brouie Please cover the remaining ui walks
Verification checklist (for QA / sign-off)
Issues noticed
cc: @PlasticDigits
mentioned in issue #207
mentioned in issue #208
mentioned in issue #213
mentioned in issue #531
mentioned in issue #533
mentioned in issue #559
mentioned in issue #587
mentioned in issue #660
mentioned in issue #661