feat(amm/ui): add pool position summary (#60)

Fixes #60
This commit is contained in:
Ricardo Guilherme Schmidt 2026-04-22 18:26:17 -03:00 committed by r4bbit
parent c50691edec
commit e75a778f7a
No known key found for this signature in database
GPG Key ID: E95F1E9447DC91A9
5 changed files with 336 additions and 1 deletions

View File

@ -1,6 +1,8 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import "components"
import "state"
Item {
id: root
@ -164,10 +166,31 @@ Item {
}
}
// Liquidity view (placeholder replaced when LP branch merges)
// Liquidity view
Item {
anchors.fill: parent
visible: navbar.currentIndex === 1
DummyPoolState {
id: poolState
}
Rectangle {
anchors.fill: parent
color: "#151515"
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 10
PoolPositionSummary {
poolState: poolState
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
}
}
}
}
}

View File

@ -0,0 +1,64 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: root
property string helpText: qsTr("This value is derived from your LP token balance, total LP supply, and current pool reserves.")
activeFocusOnTab: true
focusPolicy: Qt.StrongFocus
hoverEnabled: true
text: qsTr("?")
Accessible.name: qsTr("Why this value is an estimate")
onClicked: estimatePopup.opened ? estimatePopup.close() : estimatePopup.open()
Keys.onEscapePressed: estimatePopup.close()
contentItem: Text {
color: root.activeFocus || root.hovered || estimatePopup.opened ? "#F26A21" : "#E7E1D8"
font.bold: true
font.pixelSize: 11
horizontalAlignment: Text.AlignHCenter
text: qsTr("i")
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
border.color: root.activeFocus || estimatePopup.opened ? "#F26A21" : "#343434"
border.width: 1
color: root.pressed ? "#2A221D" : "#1D1D1D"
radius: 8
}
Popup {
id: estimatePopup
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
focus: true
modal: false
padding: 10
width: 224
x: Math.max(-width + root.width, -196)
y: root.height + 4
onClosed: root.forceActiveFocus()
background: Rectangle {
border.color: "#343434"
border.width: 1
color: "#1D1D1D"
radius: 8
}
contentItem: Text {
color: "#E7E1D8"
font.pixelSize: 12
lineHeight: 1.25
text: root.helpText
wrapMode: Text.WordWrap
}
}
}

View File

@ -0,0 +1,122 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import "../state"
Rectangle {
id: root
required property DummyPoolState poolState
readonly property string estimateHelp: qsTr("This value is an estimate from the current dummy reserves and your share of total LP supply.")
color: "#1D1D1D"
implicitHeight: content.implicitHeight + 20
radius: 8
border.color: "#343434"
border.width: 1
ColumnLayout {
id: content
anchors.fill: parent
anchors.margins: 10
spacing: 4
Text {
color: "#E7E1D8"
font.bold: true
font.pixelSize: 16
text: qsTr("Pool position")
Layout.fillWidth: true
}
Text {
color: "#F26A21"
font.pixelSize: 12
text: qsTr("You have no position in this pool")
visible: root.poolState.userLpBalance === 0
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Token A")
value: root.poolState.tokenA
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Token B")
value: root.poolState.tokenB
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Fee tier")
value: root.poolState.feeTier
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Your LP tokens")
value: root.poolState.formatInteger(root.poolState.userLpBalance)
visible: root.poolState.userLpBalance > 0
Layout.fillWidth: true
}
SummaryRow {
estimated: true
estimateHelp: root.estimateHelp
label: qsTr("Pool share")
value: root.poolState.formatPoolShare(root.poolState.poolShare)
visible: root.poolState.userLpBalance > 0
Layout.fillWidth: true
}
SummaryRow {
estimated: true
estimateHelp: root.estimateHelp
label: qsTr("Your Token A")
value: "\u2248 " + root.poolState.formatTokenAmount(root.poolState.userOwnedA, root.poolState.tokenA)
visible: root.poolState.userLpBalance > 0
Layout.fillWidth: true
}
SummaryRow {
estimated: true
estimateHelp: root.estimateHelp
label: qsTr("Your Token B")
value: "\u2248 " + root.poolState.formatTokenAmount(root.poolState.userOwnedB, root.poolState.tokenB)
visible: root.poolState.userLpBalance > 0
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Total reserve A")
value: root.poolState.formatTokenAmount(root.poolState.reserveA, root.poolState.tokenA)
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Total reserve B")
value: root.poolState.formatTokenAmount(root.poolState.reserveB, root.poolState.tokenB)
Layout.fillWidth: true
}
SummaryRow {
label: qsTr("Total LP supply")
value: root.poolState.formatInteger(root.poolState.totalLpSupply)
Layout.fillWidth: true
}
}
}

View File

@ -0,0 +1,60 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
Item {
id: root
property string label: ""
property string value: ""
property bool estimated: false
property string estimateHelp: qsTr("This value is derived from your LP token balance, total LP supply, and current pool reserves.")
implicitHeight: Math.max(18, Math.max(labelText.implicitHeight, valueGroup.implicitHeight))
RowLayout {
anchors.fill: parent
spacing: 8
Text {
id: labelText
color: "#A9A098"
elide: Text.ElideRight
font.pixelSize: 12
text: root.label
verticalAlignment: Text.AlignVCenter
Layout.fillWidth: true
}
RowLayout {
id: valueGroup
spacing: 4
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Text {
color: "#E7E1D8"
elide: Text.ElideRight
font.bold: true
font.pixelSize: 12
horizontalAlignment: Text.AlignRight
text: root.value
verticalAlignment: Text.AlignVCenter
Layout.maximumWidth: 178
}
EstimateInfoButton {
enabled: root.estimated
helpText: root.estimateHelp
opacity: root.estimated ? 1 : 0
visible: root.estimated
Layout.preferredHeight: 18
Layout.preferredWidth: root.estimated ? 18 : 0
}
}
}
}

View File

@ -0,0 +1,66 @@
import QtQuick 2.15
QtObject {
id: root
property string tokenA: "USDC"
property string tokenB: "ETH"
property string feeTier: "0.30%"
property real userLpBalance: 1118033
property real reserveA: 1000000
property real reserveB: 500
property real totalLpSupply: 22360679
readonly property real poolShare: totalLpSupply > 0 ? userLpBalance / totalLpSupply : 0
readonly property real userOwnedA: reserveA * poolShare
readonly property real userOwnedB: reserveB * poolShare
function applyAddLiquidity(actualA, actualB, mintedLp) {
const safeA = Math.max(0, Number(actualA) || 0);
const safeB = Math.max(0, Number(actualB) || 0);
const safeLp = Math.max(0, Number(mintedLp) || 0);
reserveA += safeA;
reserveB += safeB;
totalLpSupply += safeLp;
userLpBalance += safeLp;
}
function applyRemoveLiquidity(withdrawA, withdrawB, burnedLp) {
const safeA = Math.max(0, Number(withdrawA) || 0);
const safeB = Math.max(0, Number(withdrawB) || 0);
const safeLp = Math.max(0, Number(burnedLp) || 0);
reserveA = Math.max(0, reserveA - safeA);
reserveB = Math.max(0, reserveB - safeB);
totalLpSupply = Math.max(0, totalLpSupply - safeLp);
userLpBalance = Math.max(0, userLpBalance - safeLp);
}
function resetDummyState() {
tokenA = "USDC";
tokenB = "ETH";
feeTier = "0.30%";
userLpBalance = 1118033;
reserveA = 1000000;
reserveB = 500;
totalLpSupply = 22360679;
}
function formatInteger(value) {
const rounded = Math.round(Number(value) || 0);
return rounded.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function formatTokenAmount(value, token) {
return formatInteger(value) + " " + token;
}
function formatLpAmount(value) {
return formatInteger(value) + " LP";
}
function formatPoolShare(value) {
return "\u2248 " + (Math.max(0, Number(value) || 0) * 100).toFixed(2) + "%";
}
}