Frontend: uniform one-click Max with gas reserve #213
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#213
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
Unify retail Max (and optional 50%) balance actions across all human amount fields, and make Max subtract a gas reserve when the spend asset is native uluna (or when the user opts into native wrap on
/pool) so one-click fill does not strand the wallet or fail broadcast for lack of LUNC.Tracked in gap inventory:
gaps/GAP_1780023683.md— One-click “max” with gas reserve (Partial).Current codebase
Where “Max” exists today
frontend-dapp/src/pages/SwapPage.tsxfromRawAmount(balance, decimals)— 100% of wallet balance; no gas reserve foruluna/ native pay.LimitOrderEscrowAmountField.tsx→TradeOrderTicket.tsx,LimitOrdersPage.tsxonLimitAmountMax;useLimitOrderFormtracksescrowAmountSource: 'max'and re-applies on Bid/Ask switch when balance updates.TradeMarketOrderPanel.tsxLimitOrderEscrowAmountFieldwithonMax={setMarketAmountHuman}— full CW20 balance only.PoolPage.tsxSwapPage.tsxbookInputHuman.TradeMarketOrderPanel.tsxRelated gas preflight (submit-time, not Max-time)
These gates block submit when bank uluna is too low for multi-tx CW20 paths; they do not adjust Max amounts:
limitOrderNativeGasBalanceGate.ts+estimateLimitOrderPlaceSequenceUlunaFeesTotal()— GitLab #132evaluateMarketSwapNativeGasPlaceGate+estimateMarketPairSwapSequenceUlunaFeesTotal()— trade ticketprovideLiquidityNativeGasBalanceGate.ts+estimateProvideLiquidityCw20SequenceUlunaFeesTotal()— GitLab #147Fee math is centralized in
transactions.tsandterraGas.ts(estimateFeeUlunaAmountForGasLimit,getGasLimitForTx). Gas constants and broadcast invariants are documented indocs/frontend.md§ Terra Classic gas limits.Native swap / wrap paths
executeNativeSwapinrouter.ts(coins attached towrap_depositor CW20send→ router). Max on native pay must leave enough uluna for the same-tx (or multi-msg) fee envelope, not only for separate allowance txs.ulunawhile gas may still require a separate LUNC balance for CW20 allowance sequences.Why this is needed
LimitOrderEscrowAmountField; pool duplicates 50%/Max. Behavior and disabled states drift (e.g. pool disables Max at balance0, swap does not identically).insufficient fundsor sub-minimum fee failures because LUNC must also payFee.amountin the same (or subsequent) transaction(s). Users expect CEX-style Max to be spendable after gas.Constraints and guardrails
estimate*UlunaFeesTotal()/getGasLimitForTx()helpers intransactions.ts/terraGas.ts. Do not duplicate magic uluna constants in UI.fromRawAmount; never float subtraction on LUNC.uluna, Max remains full CW20 balance (gas is separate LUNC). Do not subtract CW20 for LUNC fees.ulunaor pool native-wrap is enabled for that side, Max =balanceRaw - reserveUluna(floored at0).MaxAmountContextenum (e.g.swap_native,swap_cw20,limit_place,market_swap,provide_liquidity_native_side,provide_liquidity_cw20) so each screen uses the correct fee envelope (1-tx native swap vs 2-tx CW20 vs 3-tx provide).isDecimalAmountDraft/ existing input validators (#169).maxmode — PreserveuseLimitOrderFormescrowAmountSource: 'max're-apply on side change; re-apply must use the new shared compute function so gas reserve stays correct when escrow token changes.broadcastTerraExecuteContracts.docs/frontend.mdwith a short § “Max amount / gas reserve” linking to the util and surfaces.Relevant files
frontend-dapp/src/components/trade/LimitOrderEscrowAmountField.tsxfrontend-dapp/src/pages/SwapPage.tsxfrontend-dapp/src/pages/PoolPage.tsx,frontend-dapp/src/pages/PoolPage.test.tsxfrontend-dapp/src/components/trade/TradeOrderTicket.tsx,TradeMarketOrderPanel.tsxfrontend-dapp/src/pages/LimitOrdersPage.tsxfrontend-dapp/src/hooks/useLimitOrderForm.tsfrontend-dapp/src/services/terraclassic/transactions.ts,terraGas.tslimitOrderNativeGasBalanceGate.ts,provideLiquidityNativeGasBalanceGate.tsfrontend-dapp/src/utils/formatAmount.tsfrontend-dapp/src/hooks/useNativeUlunaBalance.tsfrontend-dapp/src/services/terraclassic/router.tsgaps/GAP_1780023683.mdskills/AGENTS_TERRACLASSIC_GAS.mdRecommended direction
computeMaxSpendableHumanAmount(name TBD) in e.g.frontend-dapp/src/utils/maxSpendableAmount.ts:balanceRaw,decimals,assetIsNativeUluna,context: MaxAmountContext, optionalnativeUlunaBalancefor cross-check hints.reserveRaw = feeEnvelopeForContext(fromtransactions.ts); optional small safety margin only if already used elsewhere (prefer none unless repro requires).{ human, cappedByGas: boolean, reserveUluna }.AmountBalanceActions(Balance row + 50% optional + Max) used byLimitOrderEscrowAmountField,SwapPage, andPoolPage— single styling, disabled rules,sounds.playButtonPress().MaxAmountContextand pass native balance query when context needs reserve.computeMaxSpendableHumanAmountfor all contexts; component tests for disabled/loading; extendPoolPage.test.tsx/ swap tests for native Max cap.Acceptance criteria
/limits.ulunapay (swap, native-side pool add with wrap, native swap path) never sets an amount greater thanbalance - feeEnvelopeForThatAction.0, Max is disabled (or no-op with clear disabled state) — no negative or invalid drafts.docs/frontend.mddocuments Max/gas reserve invariants.MaxAmountContextvariants.Test plan — functional paths
/tradeor/limitslimit tab → Max—balanceeffectiveGasPriceUluna()× gas limit (#127)Automated:
npm testinfrontend-dappfor new unit/component tests; optional Playwright smoke on swap Max if E2E wallet funded.Test plan — attack / abuse vectors
limitOrderEscrowBalanceGate, pool “Exceeds wallet balance”)transactions.ts/terraGas.tsestimates; regression test compares reserve ≥estimateFeeUlunaAmountForGasLimitfor contextfromRawAmount; submit raw ≤ balanceassetIsNativeUlunaguard in unit testsisDecimalAmountDraftVerification criteria
frontend-dappVitest green; new tests named for GitLab issue IID.SwapPage.tsx/PoolPage.tsx(grep forfromRawAmount(balancein Max handlers → single helper).docs/frontend.mdPR section or anchor updated;skills/AGENTS_TERRACLASSIC_GAS.mdcross-links if reserve touches gas docs.gaps/GAP_1780023683.mdupdated to Done or Improved when merged.Labels / metadata
frontend,uxImplementation summary (merged to
main@79e66e5)Implemented GitLab #213: uniform one-click Max (and pool 50%) with native uluna gas reserve.
What changed
computeMaxSpendableHumanAmount+MaxAmountContextinfrontend-dapp/src/utils/maxSpendableAmount.ts— single source for Max math (BigInt reserve, thenfromRawAmount).AmountBalanceActionsshared UI component — balance row + optional 50% + Max (Swap, Pool, limit/market tickets,/limits).transactions.ts:estimateNativeSwapUlunaFeesTotal,estimateProvideLiquidityNativeWrapUlunaFeesTotal(aligned withterraGas.ts/ existing sequence estimates)./limits(max-mode re-apply viauseLimitEscrowMaxReapply).docs/frontend.md§ Max amount / gas reserve,skills/AGENTS_TERRACLASSIC_GAS.md, gap row Done ingaps/GAP_1780023683.md.maxSpendableAmount.test.ts(all contexts), extendedtransactions.test.ts, updatedPoolPage.test.tsx.Verification checklist
npm run test:runinfrontend-dappgreen formaxSpendableAmount.test.ts@brouie — please verify on LocalTerra when you have a moment. Leaving this issue open until sign-off.
mentioned in commit
0a1e90af7dmentioned in commit
79e66e522cmentioned in commit
e02d991ce9Verification complete (agent,
verify/issue-213worktree @308a04a)Re-verified GitLab #213 on LocalTerra (
127.0.0.1:26657, LCD1317, indexer3001) withfrontend-dappVitest and browser smoke onhttp://127.0.0.1:5174(simulated dev walletterra1x46…20k38v).What was checked
AmountBalanceActionson Swap / Pool / trade limit+market //limits;computeMaxSpendableHumanAmount+MaxAmountContext; nativeulunareserve fromtransactions.ts/terraGas.ts; CW20 Max full balance; limit max re-apply viauseLimitEscrowMaxReapply; docs (docs/frontend.md§ Max amount / gas reserve),skills/AGENTS_TERRACLASSIC_GAS.md, gapGAP_1780023683.md→ Done.npm test -- --runinfrontend-dapp— 114 files / 706 tests passed (includesmaxSpendableAmount.test.tsGitLab #213,transactions.test.tsreserve helpers).fromRawAmount(balanceMax handlers in page components.700376.545989(full CW20). Native LUNC Max →69789744.486vs bank69789800.286250LUNC — ~55.8M uluna reserve left for fees (not full balance).Checklist for humans (optional re-run)
cd frontend-dapp && npm test -- --runinsufficient funds/tradelimit + market and/limits: Max + Bid/Ask re-apply after Max/pool: 50% / Max on A/B; native wrap Max leaves LUNC reserveeffectiveGasPriceUluna()× gas limit, #127)No code changes required on this pass;
mainalready contains the implementation.Closing as verified.
Reopening for test plan item 12 only — prior agent verification did not cover Station extension signing on LocalTerra.
@brouie Please verify:
effectiveGasPriceUluna()× gas limit (#127); no bankinsufficient fundsfrom setting pay to full balanceAlready verified (agent): shared Max UI, native gas reserve math, Vitest (
maxSpendableAmount.test.ts), simulated-wallet browser smoke (CW20 full Max; native Max < balance). See note_3398966349.Close when: item 12 passes with Station on LocalTerra; all other acceptance criteria remain satisfied on
main.Closing as testing on localterra terrastation is not possible, instead should be keplr.
mentioned in issue #531
mentioned in issue #533
mentioned in issue #587
marked as related to #587
mentioned in issue #599