Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions amm-ui/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ Item {
id: root

property var tokenData: [
{ symbol: "TOK1", name: "Token 1", color: "#627eea", letter: "E", address: "0x0000000000000000000000000000000000000000", usdPrice: 2392.70 },
{ symbol: "TOK2", name: "Token 2", color: "#2775ca", letter: "$", address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", usdPrice: 1.00 },
{ symbol: "TOK3", name: "Token 3", color: "#26a17b", letter: "T", address: "0xdac17f958d2ee523a2206206994597c13d831ec7", usdPrice: 1.00 },
{ symbol: "TOK4", name: "Token 4", color: "#f7931a", letter: "B", address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", usdPrice: 63500 },
{ symbol: "TOK5", name: "Token 5", color: "#627eea", letter: "E", address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", usdPrice: 2392.70 },
{ symbol: "TOK6", name: "Token 6", color: "#9b59b6", letter: "L", address: "0x1337000000000000000000000000000000000cafe", usdPrice: 0.42 }
{ symbol: "TOK1", name: "Token 1", color: "#627eea", letter: "E", address: "0x0000000000000000000000000000000000000000", usdPrice: 2392.70, balance: 4.25, reserve: 850 },
{ symbol: "TOK2", name: "Token 2", color: "#2775ca", letter: "$", address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", usdPrice: 1.00, balance: 12480, reserve: 2400000 },
{ symbol: "TOK3", name: "Token 3", color: "#26a17b", letter: "T", address: "0xdac17f958d2ee523a2206206994597c13d831ec7", usdPrice: 1.00, balance: 320, reserve: 1800000 },
{ symbol: "TOK4", name: "Token 4", color: "#f7931a", letter: "B", address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", usdPrice: 63500, balance: 0.18, reserve: 42 },
{ symbol: "TOK5", name: "Token 5", color: "#627eea", letter: "E", address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", usdPrice: 2392.70, balance: 0, reserve: 600 },
{ symbol: "TOK6", name: "Token 6", color: "#9b59b6", letter: "L", address: "0x1337000000000000000000000000000000000cafe", usdPrice: 0.42, balance: 5400, reserve: 950000 }
]

// ── Navigation bar ────────────────────────────────────────────────────────
Expand Down Expand Up @@ -124,6 +124,10 @@ Item {
tokenModal.targetSide = side
tokenModal.open()
}

onSubmitRequested: function(snapshot) {
swapConfirmationDialog.openWithSnapshot(snapshot)
}
}

Text {
Expand All @@ -150,6 +154,34 @@ Item {
tokenModal.close()
}
}

SuccessToast {
id: swapToast

width: Math.max(0, Math.min(380, parent.width - 32))

anchors {
bottom: parent.bottom
bottomMargin: 24
horizontalCenter: parent.horizontalCenter
}
}

SwapConfirmationDialog {
id: swapConfirmationDialog
anchors.fill: parent
theme: theme

onConfirmed: function(snapshot) {
swapCard.resetAmounts()
swapToast.show(qsTr("Swap submitted"),
qsTr("%1 %2 → %3 %4")
.arg(snapshot.sellAmount)
.arg(snapshot.sellToken)
.arg(snapshot.minReceived)
.arg(snapshot.buyToken))
}
}
}
}

Expand Down
88 changes: 74 additions & 14 deletions amm-ui/qml/SwapCard.qml
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,91 @@ Rectangle {
property var sellToken: null
property var buyToken: null
property string sellAmount: ""
property real slippageTolerancePercent: 0.5
readonly property real feePercent: 0.30

signal requestTokenSelect(string side)
signal submitRequested(var snapshot)

function setToken(side, token) {
if (side === "sell") root.sellToken = token
else root.buyToken = token
}

function resetAmounts() {
root.sellAmount = ""
}

readonly property real parsedSellAmount: {
var amt = parseFloat(sellAmount)
return isNaN(amt) || amt < 0 ? 0 : amt
}

readonly property real parsedBuyAmount: {
if (!sellToken || !buyToken || parsedSellAmount <= 0) return 0
return parsedSellAmount * sellToken.usdPrice / buyToken.usdPrice
}

readonly property real minReceivedAmount: parsedBuyAmount * (1 - slippageTolerancePercent / 100)

readonly property real priceImpactPercent: {
if (!sellToken || parsedSellAmount <= 0) return 0
var reserve = sellToken.reserve || 0
if (reserve <= 0) return 0
return parsedSellAmount / (reserve + parsedSellAmount) * 100
}

readonly property bool hasAmount: parsedSellAmount > 0
readonly property bool tokensSelected: sellToken !== null && buyToken !== null
readonly property bool insufficientBalance: hasAmount && sellToken !== null && parsedSellAmount > (sellToken.balance || 0)
readonly property bool insufficientLiquidity: hasAmount && buyToken !== null && parsedBuyAmount > (buyToken.reserve || 0)
readonly property bool canSubmit: tokensSelected && hasAmount && !insufficientBalance && !insufficientLiquidity

readonly property string submitButtonText: {
if (!hasAmount || !tokensSelected) return qsTr("Enter an amount")
if (insufficientBalance) return qsTr("Insufficient balance")
if (insufficientLiquidity) return qsTr("Insufficient liquidity")
return qsTr("Swap")
}

function formatAmountValue(val) {
if (val >= 1) return val.toFixed(2)
if (val >= 0.0001) return val.toFixed(6)
return val.toFixed(8)
}

readonly property string buyAmount: {
if (!sellToken || !buyToken || sellAmount === "") return ""
var amt = parseFloat(sellAmount)
if (isNaN(amt) || amt <= 0) return ""
var result = amt * sellToken.usdPrice / buyToken.usdPrice
return result >= 1 ? result.toFixed(2) : result.toFixed(6)
if (parsedSellAmount <= 0) return ""
return formatAmountValue(parsedBuyAmount)
}

readonly property string sellUsd: {
if (!sellToken || sellAmount === "") return ""
var amt = parseFloat(sellAmount)
if (isNaN(amt)) return ""
var val = amt * sellToken.usdPrice
if (parsedSellAmount <= 0) return ""
var val = parsedSellAmount * sellToken.usdPrice
return "~$" + val.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}

readonly property string buyUsd: {
if (!buyToken || buyAmount === "") return ""
var amt = parseFloat(buyAmount)
if (isNaN(amt)) return ""
var val = amt * buyToken.usdPrice
var val = parsedBuyAmount * buyToken.usdPrice
return "~$" + val.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}

function buildSnapshot() {
return {
"sellToken": sellToken ? sellToken.symbol : "",
"buyToken": buyToken ? buyToken.symbol : "",
"sellAmount": formatAmountValue(parsedSellAmount),
"buyAmount": formatAmountValue(parsedBuyAmount),
"minReceived": formatAmountValue(minReceivedAmount),
"feePercent": feePercent.toFixed(2) + "%",
"priceImpactPercent": priceImpactPercent < 0.01 ? "<0.01%" : priceImpactPercent.toFixed(2) + "%",
"slippageTolerance": slippageTolerancePercent.toFixed(2).replace(/0+$/, "").replace(/[.]$/, "") + "%"
}
}

radius: 24
color: theme.colors.cardBg
border.color: theme.colors.border
Expand Down Expand Up @@ -124,20 +177,23 @@ Rectangle {
}

Rectangle {
id: ctaBox
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 8
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.preferredHeight: 56
radius: 20
color: ctaHover.containsMouse ? theme.colors.ctaHoverBg : theme.colors.ctaBg
color: !root.canSubmit ? theme.colors.panelBg
: ctaHover.containsMouse ? theme.colors.ctaHoverBg
: theme.colors.ctaBg
Behavior on color { ColorAnimation { duration: 120 } }

Text {
anchors.centerIn: parent
text: "Swap"
color: "#ffffff"
text: root.submitButtonText
color: root.canSubmit ? "#ffffff" : theme.colors.textSecondary
font.pixelSize: 17
font.weight: Font.Medium
}
Expand All @@ -146,7 +202,11 @@ Rectangle {
id: ctaHover
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: root.canSubmit
cursorShape: root.canSubmit ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (root.canSubmit) root.submitRequested(root.buildSnapshot())
}
}
}
}
Expand Down
Loading
Loading