fix(amm-ui): require explicit liquidity inputs

Keep wallet assets as token choices without automatically selecting a pair. Keep pool-probe amounts internal so active-pool quotes do not populate the form before user input.
This commit is contained in:
Ricardo Guilherme Schmidt 2026-07-15 21:24:56 -03:00
parent 27cd79efdd
commit 57855e6073
No known key found for this signature in database
GPG Key ID: 1396EA17DE132FFE
2 changed files with 55 additions and 52 deletions

View File

@ -109,13 +109,13 @@ AmmActionCard {
implicitHeight: content.implicitHeight + root.contentPadding * 2
implicitWidth: 480
Component.onCompleted: Qt.callLater(root.ensurePair)
Component.onCompleted: Qt.callLater(root.reconcileSelection)
onNewPositionContextChanged: Qt.callLater(root.applyContextChange)
function applyContextChange() {
if (root.resolvingToken)
root.finishTokenResolution()
else
root.ensurePair()
root.reconcileSelection()
}
onQuotePayloadChanged: {
if (root.quoteStale)
@ -786,26 +786,23 @@ AmmActionCard {
root.tokenResolutionErrorSide = side
}
function ensurePair() {
function reconcileSelection() {
var status = String(root.newPositionContext.status || "")
if (status !== "ready" && status !== "no_wallet")
return
var previousA = root.selectedTokenAId
var previousB = root.selectedTokenBId
var selectable = root.selectableTokenIds()
if (selectable.length === 0) {
if (root.selectedTokenAId.length > 0
&& selectable.indexOf(root.selectedTokenAId) < 0) {
root.selectedTokenAId = ""
}
if (root.selectedTokenBId.length > 0
&& selectable.indexOf(root.selectedTokenBId) < 0) {
root.selectedTokenBId = ""
if (previousA.length > 0 || previousB.length > 0)
root.resetPairDraft()
return
}
if (selectable.indexOf(root.selectedTokenAId) < 0)
root.selectedTokenAId = selectable[0]
if (selectable.indexOf(root.selectedTokenBId) < 0
|| root.selectedTokenBId === root.selectedTokenAId) {
root.selectedTokenBId = selectable.length > 1 ? selectable[1] : ""
}
if (root.selectedTokenBId === root.selectedTokenAId)
root.selectedTokenBId = ""
if (root.selectedTokenAId !== previousA || root.selectedTokenBId !== previousB)
root.resetPairDraft()
else if (root.hasPair)
@ -987,14 +984,7 @@ AmmActionCard {
return { "ok": false, "errors": errors, "request": ({}) }
}
var canonicalAId = root.displayIsCanonical ? root.selectedTokenAId : root.selectedTokenBId
var canonicalBId = root.displayIsCanonical ? root.selectedTokenBId : root.selectedTokenAId
var request = {
"schema": "new-position.v1",
"tokenAId": canonicalAId,
"tokenBId": canonicalBId,
"feeBps": root.selectedFeeBps
}
var request = root.pairRequest()
if (root.activePool) {
var parsedA = AmountMath.parseHuman(root.amountA, root.decimalsA)
@ -1085,6 +1075,17 @@ AmmActionCard {
return { "ok": errors.length === 0, "errors": errors, "request": request }
}
function pairRequest() {
return {
"schema": "new-position.v1",
"tokenAId": root.displayIsCanonical
? root.selectedTokenAId : root.selectedTokenBId,
"tokenBId": root.displayIsCanonical
? root.selectedTokenBId : root.selectedTokenAId,
"feeBps": root.selectedFeeBps
}
}
function requestQuote(immediate) {
if (root.activePool && root.amountA.length === 0 && root.amountB.length === 0) {
root.localErrors = []
@ -1327,24 +1328,18 @@ AmmActionCard {
return
if (root.poolFeeBps > 0 && root.selectedFeeBps !== root.poolFeeBps) {
root.selectedFeeBps = root.poolFeeBps
if (root.amountA.length === 0 && root.amountB.length === 0) {
root.amountA = AmountMath.formatRaw(
root.probeRaw(root.tokenA, root.decimalsA), root.decimalsA)
root.amountB = AmountMath.formatRaw(
root.probeRaw(root.tokenB, root.decimalsB), root.decimalsB)
}
root.requestQuote(true)
root.localErrors = []
root.quoteRequested(true, {
"ok": true,
"errors": [],
"request": root.poolProbeRequest(root.pairRequest())
})
return
}
if (root.quotePayload.status !== "ok")
return
if (root.activePool && root.amountA.length === 0 && root.amountB.length === 0) {
root.amountA = AmountMath.formatRaw(root.displayRaw("maxAmountARaw", "maxAmountBRaw", "A"), root.decimalsA)
root.amountB = AmountMath.formatRaw(root.displayRaw("maxAmountARaw", "maxAmountBRaw", "B"), root.decimalsB)
}
if (root.missingPool) {
var rawA = root.displayRaw("actualAmountARaw", "actualAmountBRaw", "A")
var rawB = root.displayRaw("actualAmountARaw", "actualAmountBRaw", "B")

View File

@ -91,17 +91,33 @@ TestCase {
}
}
function createForm() {
function createEmptyForm(context) {
var form = createTemporaryObject(formComponent, testCase, {
"flowState": flowState(({}))
"flowState": flowState(({})),
"newPositionContext": context || readyContext()
})
verify(form)
wait(0)
return form
}
function createForm(context) {
var form = createEmptyForm(context)
form.selectToken("A", tokenLow)
form.selectToken("B", tokenHigh)
compare(form.selectedTokenAId, tokenLow)
compare(form.selectedTokenBId, tokenHigh)
return form
}
function test_walletTokensDoNotPrefillPosition() {
var form = createEmptyForm()
compare(form.selectedTokenAId, "")
compare(form.selectedTokenBId, "")
compare(form.amountA, "")
compare(form.amountB, "")
}
function test_displayOrderMapsToCanonicalRequestAfterSwap() {
var form = createForm()
var built = form.buildQuoteRequest()
@ -159,12 +175,7 @@ TestCase {
}
function test_missingPoolAcceptsLargeDirectAmountsFromEitherSide() {
var form = createTemporaryObject(formComponent, testCase, {
"flowState": flowState(({})),
"newPositionContext": rawAmountsContext()
})
verify(form)
wait(0)
var form = createForm(rawAmountsContext())
form.priceAmountA = "15"
form.priceAmountB = "10"
form.flowState = flowState({
@ -199,12 +210,7 @@ TestCase {
}
function test_missingPoolRoundsPairedRawAmounts() {
var form = createTemporaryObject(formComponent, testCase, {
"flowState": flowState(({})),
"newPositionContext": rawAmountsContext()
})
verify(form)
wait(0)
var form = createForm(rawAmountsContext())
form.priceAmountA = "15"
form.priceAmountB = "10"
form.flowState = flowState({
@ -415,14 +421,14 @@ TestCase {
var form = createForm()
form.flowState = flowState(quote)
wait(0)
compare(form.amountA, "20")
compare(form.amountB, "4")
compare(form.amountA, "")
compare(form.amountB, "")
quoteRequestedSpy.target = form
quoteRequestedSpy.clear()
form.editActiveAmount("A", "5")
compare(form.amountA, "5")
compare(form.amountB, "4")
compare(form.amountB, "")
compare(quoteRequestedSpy.count, 0)
form.finishActiveAmount("A", "5")
@ -509,6 +515,8 @@ TestCase {
wait(0)
compare(form.selectedFeeBps, 5)
compare(form.amountA, "")
compare(form.amountB, "")
compare(quoteRequestedSpy.count, 1)
verify(quoteRequestedSpy.signalArguments[0][1].ok)
compare(quoteRequestedSpy.signalArguments[0][1].request.maxAmountARaw, "5000000000")
@ -582,8 +590,8 @@ TestCase {
}
wait(0)
compare(form.selectedTokenAId, tokenHigh)
compare(form.selectedTokenBId, tokenThird)
compare(form.selectedTokenAId, "")
compare(form.selectedTokenBId, tokenHigh)
compare(form.amountA, "")
compare(form.amountB, "")
compare(form.minimumAmountARaw, "")