From 78fd86f56b0e0e4316b8588047f0437ce84a1e13 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Sat, 18 Jul 2026 12:05:05 -0300 Subject: [PATCH] fix(liquidity): move quote details to confirmation --- .../LiquidityConfirmationSummary.qml | 101 +++++++++- .../components/liquidity/NewPositionForm.qml | 181 +++++------------- apps/amm/qml/pages/LiquidityPage.qml | 2 +- apps/amm/qml/state/NewPositionFlow.qml | 1 + .../qml/tst_LiquidityConfirmationDialog.qml | 58 ++++++ apps/amm/tests/qml/tst_LiquidityPage.qml | 19 +- apps/amm/tests/qml/tst_NewPositionForm.qml | 15 +- 7 files changed, 245 insertions(+), 132 deletions(-) diff --git a/apps/amm/qml/components/liquidity/LiquidityConfirmationSummary.qml b/apps/amm/qml/components/liquidity/LiquidityConfirmationSummary.qml index 61e5ca2..39ce86d 100644 --- a/apps/amm/qml/components/liquidity/LiquidityConfirmationSummary.qml +++ b/apps/amm/qml/components/liquidity/LiquidityConfirmationSummary.qml @@ -4,6 +4,8 @@ import QtQuick import QtQuick.Controls.Basic import QtQuick.Layouts +import Logos.Controls + ColumnLayout { id: root @@ -75,8 +77,9 @@ ColumnLayout { } SummaryRow { + objectName: "confirmationDeposit" Layout.fillWidth: true - label: qsTr("Deposit") + label: root.snapshot.depositLabel || qsTr("Deposit") value: qsTr("%1 + %2") .arg(root.snapshot.depositAText || "-") .arg(root.snapshot.depositBText || "-") @@ -84,11 +87,107 @@ ColumnLayout { } SummaryRow { + objectName: "confirmationInitialPrice" + Layout.fillWidth: true + visible: root.isMissingPool() && String(root.snapshot.initialPriceText || "").length > 0 + label: qsTr("Initial price") + value: root.snapshot.initialPriceText || "-" + valueWrapAnywhere: true + } + + SummaryRow { + objectName: "confirmationInversePrice" + Layout.fillWidth: true + visible: root.isMissingPool() && String(root.snapshot.inverseInitialPriceText || "").length > 0 + label: qsTr("Inverse price") + value: root.snapshot.inverseInitialPriceText || "-" + valueWrapAnywhere: true + } + + SummaryRow { + objectName: "confirmationDepositMultiplier" + Layout.fillWidth: true + visible: root.isMissingPool() && String(root.snapshot.depositMultiplierText || "").length > 0 + label: qsTr("Deposit multiplier") + value: root.snapshot.depositMultiplierText || "-" + } + + SummaryRow { + objectName: "confirmationDepositScale" + Layout.fillWidth: true + visible: root.isMissingPool() && String(root.snapshot.depositScaleText || "").length > 0 + label: qsTr("Deposit scale") + value: root.snapshot.depositScaleText || "-" + } + + SummaryRow { + objectName: "confirmationExpectedLp" Layout.fillWidth: true label: qsTr("Expected LP") value: root.snapshot.expectedLpText || "-" } + SummaryRow { + objectName: "confirmationLpGuard" + Layout.fillWidth: true + label: root.snapshot.lpGuardLabel || qsTr("Minimum LP") + value: root.snapshot.lpGuardText || "-" + } + + SummaryRow { + objectName: "confirmationPool" + Layout.fillWidth: true + visible: String(root.snapshot.poolId || "").length > 0 + label: qsTr("Pool") + value: String(root.snapshot.poolId || "") + valueWrapAnywhere: true + } + + LogosButton { + id: accountPlanButton + + objectName: "confirmationAccountPlan" + Layout.alignment: Qt.AlignLeft + visible: root.accountPlan().length > 0 + text: qsTr("Account plan (%1)").arg(root.accountPlan().length) + property bool expanded: false + implicitWidth: 150 + implicitHeight: 36 + radius: 6 + onClicked: expanded = !expanded + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 5 + visible: accountPlanButton.visible && accountPlanButton.expanded + + Repeater { + model: root.accountPlan() + + SummaryRow { + required property var modelData + + Layout.fillWidth: true + label: qsTr("%1. %2 · %3") + .arg(Number(modelData.order || 0) + 1) + .arg(String(modelData.role || "-")) + .arg(String(modelData.action || "-")) + value: modelData.accountId ? String(modelData.accountId) + : qsTr("Assigned by wallet") + valueWrapAnywhere: true + } + } + } + + function isMissingPool() { + return root.snapshot.poolStatus === "missing_pool" + } + + function accountPlan() { + return root.snapshot.accountPreview || [] + } + function destinationRows() { var rows = [] var options = root.snapshot.lpHoldingOptions || [] diff --git a/apps/amm/qml/components/liquidity/NewPositionForm.qml b/apps/amm/qml/components/liquidity/NewPositionForm.qml index 35f6428..f2f0a7e 100644 --- a/apps/amm/qml/components/liquidity/NewPositionForm.qml +++ b/apps/amm/qml/components/liquidity/NewPositionForm.qml @@ -548,96 +548,6 @@ AmmActionCard { } } - ColumnLayout { - Layout.fillWidth: true - spacing: 9 - visible: root.quotePayload.status === "ok" - && root.quoteMatchesPair() - && !root.quoteStale - - Rectangle { - Layout.fillWidth: true - implicitHeight: 1 - color: root.theme.colors.divider - } - - LabelValueRow { - label: root.activePool ? qsTr("Expected spend") : qsTr("Opening deposit") - value: root.depositSummary() - } - - LabelValueRow { - visible: root.missingPool - label: qsTr("Initial price") - value: root.initialPriceText(false) - } - - LabelValueRow { - visible: root.missingPool - label: qsTr("Inverse price") - value: root.initialPriceText(true) - } - - LabelValueRow { - visible: root.missingPool - label: qsTr("Deposit multiplier") - value: root.depositMultiplierValue() - } - - LabelValueRow { - visible: root.missingPool - label: qsTr("Deposit scale") - value: root.depositBasisPointsText() - } - - LabelValueRow { - label: qsTr("Expected LP") - value: root.rawLpText(root.quotePayload.expectedLpRaw) - } - - LabelValueRow { - label: root.activePool ? qsTr("Minimum LP") : qsTr("Locked LP") - value: root.rawLpText(root.activePool - ? root.quotePayload.minimumLpRaw - : root.quotePayload.lockedLpRaw) - } - - LabelValueRow { - label: qsTr("Pool") - value: String(root.quotePayload.poolId || "") - valueWrapAnywhere: true - } - - LogosButton { - id: accountPlanButton - text: qsTr("Account plan (%1)").arg(root.accountPreview().length) - enabled: root.accountPreview().length > 0 - property bool checked: false - implicitWidth: 150 - implicitHeight: 36 - radius: 6 - Layout.alignment: Qt.AlignLeft - onClicked: checked = !checked - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 5 - visible: accountPlanButton.checked - - Repeater { - model: root.accountPreview() - - LabelValueRow { - required property var modelData - label: qsTr("%1. %2 · %3").arg(modelData.order + 1).arg(modelData.role).arg(modelData.action) - value: modelData.accountId ? modelData.accountId : qsTr("Assigned by wallet") - valueWrapAnywhere: true - } - } - } - } - Rectangle { Layout.fillWidth: true implicitHeight: warningTextItem.implicitHeight + 20 @@ -683,32 +593,6 @@ AmmActionCard { } } - component LabelValueRow: RowLayout { - required property string label - required property string value - property bool valueWrapAnywhere: false - Layout.fillWidth: true - spacing: 12 - - Text { - text: parent.label - color: root.theme.colors.textSecondary - font.pixelSize: 12 - Layout.fillWidth: true - wrapMode: Text.Wrap - } - - Text { - text: parent.value - color: root.theme.colors.textPrimary - font.pixelSize: 12 - font.weight: Font.Medium - horizontalAlignment: Text.AlignRight - wrapMode: parent.valueWrapAnywhere ? Text.WrapAnywhere : Text.Wrap - Layout.maximumWidth: root.compact ? 190 : 280 - } - } - component PriceRatioAdjustment: RowLayout { id: ratioAdjustment @@ -1531,20 +1415,21 @@ AmmActionCard { } function quoteAmount(canonicalAField, canonicalBField, side) { + return root.quoteAmountFromQuote(root.quotePayload, + canonicalAField, + canonicalBField, + side) + } + + function quoteAmountFromQuote(quote, canonicalAField, canonicalBField, side) { var token = side === "A" ? root.tokenA : root.tokenB var decimals = side === "A" ? root.decimalsA : root.decimalsB - var raw = root.displayRaw(canonicalAField, canonicalBField, side) + var raw = root.displayQuoteRaw(quote, canonicalAField, canonicalBField, side) return raw.length > 0 ? qsTr("%1 %2").arg(AmountMath.formatRaw(raw, decimals)).arg(root.shortTokenName(token)) : "—" } - function depositSummary() { - var amountA = root.quoteAmount("actualAmountARaw", "actualAmountBRaw", "A") - var amountB = root.quoteAmount("actualAmountARaw", "actualAmountBRaw", "B") - return amountA + " + " + amountB - } - function initialPriceText(inverse) { var price = inverse ? root.inverseInitialPrice : root.initialPrice if (price.length === 0) @@ -1603,19 +1488,59 @@ AmmActionCard { return warnings.length > 0 ? root.issueText(warnings[0].code) : "" } + function confirmationDetails(quote) { + var sourceQuote = quote || ({}) + var status = String(sourceQuote.poolStatus || root.poolStatus || "") + var isActivePool = status === "active_pool" + var accountPlan = sourceQuote.accountPreview || [] + return { + "poolStatus": status, + "depositLabel": isActivePool ? qsTr("Expected spend") : qsTr("Opening deposit"), + "depositAText": root.quoteAmountFromQuote(sourceQuote, + "actualAmountARaw", + "actualAmountBRaw", + "A"), + "depositBText": root.quoteAmountFromQuote(sourceQuote, + "actualAmountARaw", + "actualAmountBRaw", + "B"), + "initialPriceText": isActivePool ? "" : root.initialPriceText(false), + "inverseInitialPriceText": isActivePool ? "" : root.initialPriceText(true), + "depositMultiplierText": isActivePool ? "" : root.depositMultiplierValue(), + "depositScaleText": isActivePool ? "" : root.depositBasisPointsText(), + "expectedLpText": root.rawLpText(sourceQuote.expectedLpRaw), + "lpGuardLabel": isActivePool ? qsTr("Minimum LP") : qsTr("Locked LP"), + "lpGuardText": root.rawLpText(isActivePool + ? sourceQuote.minimumLpRaw + : sourceQuote.lockedLpRaw), + "poolId": String(sourceQuote.poolId || ""), + "accountPreview": accountPlan + } + } + + function refreshConfirmationSnapshot(snapshot) { + var refreshed = {} + for (var field in snapshot) + refreshed[field] = snapshot[field] + var sourceQuote = refreshed.quote || root.quotePayload + refreshed.quote = sourceQuote + var details = root.confirmationDetails(sourceQuote) + for (var detail in details) + refreshed[detail] = details[detail] + return refreshed + } + function submissionSnapshot() { var built = root.buildQuoteRequest() var poolProbe = root.poolProbeRequest(built.request) poolProbe.poolId = String(root.quotePayload.poolId || "") - return { + return root.refreshConfirmationSnapshot({ "request": built.request, "poolProbeRequest": poolProbe, + "quote": root.quotePayload, "quoteHash": String(root.quotePayload.quoteHash || ""), "pairText": qsTr("%1 / %2").arg(root.shortTokenName(root.tokenA)).arg(root.shortTokenName(root.tokenB)), "feeText": root.feeLabel(root.selectedFeeBps), - "depositAText": root.quoteAmount("actualAmountARaw", "actualAmountBRaw", "A"), - "depositBText": root.quoteAmount("actualAmountARaw", "actualAmountBRaw", "B"), - "expectedLpText": root.rawLpText(root.quotePayload.expectedLpRaw), "instruction": String(root.quotePayload.instruction || ""), "lpHoldingOptions": root.quotePayload.lpHoldingOptions || [], "selectedLpHoldingId": String(root.quotePayload.selectedLpHoldingId || ""), @@ -1623,7 +1548,7 @@ AmmActionCard { && root.quotePayload.lpDestinationRequired !== true, "lpDestinationRequired": root.quotePayload.lpDestinationRequired === true, "quoteReady": root.quotePayload.canSubmit === true - } + }) } function onlyLpDestinationBlocks() { diff --git a/apps/amm/qml/pages/LiquidityPage.qml b/apps/amm/qml/pages/LiquidityPage.qml index 02c9985..17df4f0 100644 --- a/apps/amm/qml/pages/LiquidityPage.qml +++ b/apps/amm/qml/pages/LiquidityPage.qml @@ -253,7 +253,7 @@ Item { } function onConfirmationQuoteReady(snapshot) { - confirmationDialog.updateSnapshot(snapshot) + confirmationDialog.updateSnapshot(form.refreshConfirmationSnapshot(snapshot)) } function onSubmitSucceeded() { diff --git a/apps/amm/qml/state/NewPositionFlow.qml b/apps/amm/qml/state/NewPositionFlow.qml index 0f0ddc9..e257d28 100644 --- a/apps/amm/qml/state/NewPositionFlow.qml +++ b/apps/amm/qml/state/NewPositionFlow.qml @@ -291,6 +291,7 @@ QtObject { var refreshed = {} for (var field in snapshot) refreshed[field] = snapshot[field] + refreshed.quote = quote refreshed.quoteHash = String(quote.quoteHash || "") refreshed.expectedLpText = String(quote.expectedLpRaw || "") + " raw LP" refreshed.instruction = String(quote.instruction || "") diff --git a/apps/amm/tests/qml/tst_LiquidityConfirmationDialog.qml b/apps/amm/tests/qml/tst_LiquidityConfirmationDialog.qml index 3830865..9bc92b9 100644 --- a/apps/amm/tests/qml/tst_LiquidityConfirmationDialog.qml +++ b/apps/amm/tests/qml/tst_LiquidityConfirmationDialog.qml @@ -79,4 +79,62 @@ TestCase { verify(picker) compare(picker.background.color, summary.theme.colors.panelBg) } + + function test_confirmationShowsQuoteDetails() { + var summary = createTemporaryObject(summaryComponent, testCase, { + "snapshot": { + "instruction": "NewDefinition", + "poolStatus": "missing_pool", + "depositLabel": "Opening deposit", + "depositAText": "2 Low", + "depositBText": "3 High", + "initialPriceText": "1 Low = 1.5 High", + "inverseInitialPriceText": "1 High = 0.66 Low", + "depositMultiplierText": "2x minimum", + "depositScaleText": "20000 basis points", + "expectedLpText": "10 raw LP", + "lpGuardLabel": "Locked LP", + "lpGuardText": "2 raw LP", + "poolId": "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE", + "accountPreview": [{ + "order": 0, + "role": "LP holding", + "action": "create", + "accountId": "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE" + }] + } + }) + verify(summary) + + var deposit = findChild(summary, "confirmationDeposit") + var initialPrice = findChild(summary, "confirmationInitialPrice") + var inversePrice = findChild(summary, "confirmationInversePrice") + var multiplier = findChild(summary, "confirmationDepositMultiplier") + var scale = findChild(summary, "confirmationDepositScale") + var expectedLp = findChild(summary, "confirmationExpectedLp") + var lpGuard = findChild(summary, "confirmationLpGuard") + var pool = findChild(summary, "confirmationPool") + var accountPlan = findChild(summary, "confirmationAccountPlan") + + verify(deposit) + verify(initialPrice) + verify(inversePrice) + verify(multiplier) + verify(scale) + verify(expectedLp) + verify(lpGuard) + verify(pool) + verify(accountPlan) + compare(deposit.label, "Opening deposit") + compare(deposit.value, "2 Low + 3 High") + compare(initialPrice.value, "1 Low = 1.5 High") + compare(inversePrice.value, "1 High = 0.66 Low") + compare(multiplier.value, "2x minimum") + compare(scale.value, "20000 basis points") + compare(expectedLp.value, "10 raw LP") + compare(lpGuard.label, "Locked LP") + compare(lpGuard.value, "2 raw LP") + compare(pool.value, "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE") + compare(accountPlan.text, "Account plan (1)") + } } diff --git a/apps/amm/tests/qml/tst_LiquidityPage.qml b/apps/amm/tests/qml/tst_LiquidityPage.qml index 1cc4862..1c59cd0 100644 --- a/apps/amm/tests/qml/tst_LiquidityPage.qml +++ b/apps/amm/tests/qml/tst_LiquidityPage.qml @@ -289,7 +289,14 @@ TestCase { "request": ({ "schema": "new-position.v2" }), "quoteHash": "sha256:stale", "expectedLpText": "10 raw LP", - "instruction": "AddLiquidity" + "instruction": "AddLiquidity", + "poolId": "11111111111111111111111111111111", + "accountPreview": [{ + "order": 0, + "role": "LP holding", + "action": "create", + "accountId": "11111111111111111111111111111111" + }] }) tryCompare(dialog, "opened", true) @@ -307,6 +314,13 @@ TestCase { "quoteHash": "sha256:fresh", "expectedLpRaw": "20", "instruction": "AddLiquidity", + "poolId": "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE", + "accountPreview": [{ + "order": 0, + "role": "LP holding", + "action": "reuse", + "accountId": "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE" + }], "lpHoldingOptions": [], "selectedLpHoldingId": "", "requiresFreshLp": true, @@ -319,6 +333,9 @@ TestCase { tryCompare(dialog, "opened", true) compare(dialog.snapshot.quoteHash, "sha256:fresh") compare(dialog.snapshot.expectedLpText, "20 raw LP") + compare(dialog.snapshot.poolId, "1thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE") + compare(dialog.snapshot.accountPreview.length, 1) + compare(dialog.snapshot.accountPreview[0].action, "reuse") compare(dialog.snapshot.quoteReady, true) compare(page.flow.flowErrorCode, "") } diff --git a/apps/amm/tests/qml/tst_NewPositionForm.qml b/apps/amm/tests/qml/tst_NewPositionForm.qml index 0644495..65659ce 100644 --- a/apps/amm/tests/qml/tst_NewPositionForm.qml +++ b/apps/amm/tests/qml/tst_NewPositionForm.qml @@ -657,13 +657,26 @@ TestCase { "quoteHash": "sha256:expected", "actualAmountARaw": "2000000", "actualAmountBRaw": "3", - "expectedLpRaw": "10" + "expectedLpRaw": "10", + "lockedLpRaw": "2", + "accountPreview": [{ + "order": 0, + "role": "LP holding", + "action": "create", + "accountId": submittedTransactionId + }] }) wait(0) var snapshot = form.submissionSnapshot() compare(snapshot.poolProbeRequest.poolId, submittedTransactionId) verify(snapshot.request.poolId === undefined) + compare(snapshot.depositLabel, "Opening deposit") + compare(snapshot.lpGuardLabel, "Locked LP") + compare(snapshot.lpGuardText, "2 raw LP") + compare(snapshot.poolId, submittedTransactionId) + compare(snapshot.accountPreview.length, 1) + compare(snapshot.accountPreview[0].accountId, submittedTransactionId) } function test_staleQuoteErrorsDoNotMarkCurrentDraft() {