mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-17 04:19:35 +00:00
refactor(amm-ui): defer token decimal inference
Keep token balances and amounts in raw integer units until the decimal display model receives dedicated UX analysis.
This commit is contained in:
parent
7c98ca7b42
commit
0c4c916ae4
@ -46,9 +46,6 @@ transaction ID is displayed.
|
||||
scales either deposit from the minimum that mints more than
|
||||
`MINIMUM_LIQUIDITY`.
|
||||
- Active Pool flow keeps the reserve ratio and previews expected LP output.
|
||||
- Decimal inference keeps small supplies at 0 decimals, medium supplies at 6,
|
||||
large supplies at 9, and massive supplies at 18.
|
||||
- Human amount parse/format preserves 0, 6, 9, and 18 decimal values.
|
||||
- `new_definition` and `add_liquidity` account lists match the committed AMM IDL
|
||||
order.
|
||||
- Submit re-quotes and surfaces `quote_changed` when the request no longer
|
||||
|
||||
@ -133,19 +133,6 @@ function pow10(exponent) {
|
||||
return value
|
||||
}
|
||||
|
||||
function implyDecimals(totalSupplyRaw) {
|
||||
if (!isUnsigned(totalSupplyRaw))
|
||||
return 0
|
||||
var digits = normalize(totalSupplyRaw).length
|
||||
if (digits <= 9)
|
||||
return 0
|
||||
if (digits <= 14)
|
||||
return 6
|
||||
if (digits <= 21)
|
||||
return 9
|
||||
return 18
|
||||
}
|
||||
|
||||
function parseHuman(text, decimals) {
|
||||
var value = String(text)
|
||||
if (value.length === 0)
|
||||
@ -163,17 +150,6 @@ function parseHuman(text, decimals) {
|
||||
return { "ok": true, "code": "", "raw": raw }
|
||||
}
|
||||
|
||||
function trimHumanPrecision(text, decimals) {
|
||||
var value = String(text)
|
||||
var match = /^(0|[1-9][0-9]*)(?:\.([0-9]*))?$/.exec(value)
|
||||
if (!match)
|
||||
return value
|
||||
var fraction = match[2] || ""
|
||||
if (fraction.length <= decimals)
|
||||
return value
|
||||
return decimals > 0 ? match[1] + "." + fraction.slice(0, decimals) : match[1]
|
||||
}
|
||||
|
||||
function formatRaw(rawValue, decimals) {
|
||||
if (!isUnsigned(rawValue))
|
||||
return ""
|
||||
|
||||
@ -65,8 +65,8 @@ AmmActionCard {
|
||||
? root.newPositionContext.feeTiers : []
|
||||
readonly property var tokenA: root.tokenById(root.selectedTokenAId)
|
||||
readonly property var tokenB: root.tokenById(root.selectedTokenBId)
|
||||
readonly property int decimalsA: AmountMath.implyDecimals(root.tokenA.totalSupplyRaw || "0")
|
||||
readonly property int decimalsB: AmountMath.implyDecimals(root.tokenB.totalSupplyRaw || "0")
|
||||
readonly property int decimalsA: 0
|
||||
readonly property int decimalsB: 0
|
||||
readonly property bool displayIsCanonical: root.selectedTokenAId.length > 0
|
||||
&& AmountMath.compareBase58Ids(root.selectedTokenAId,
|
||||
root.selectedTokenBId) > 0
|
||||
@ -770,11 +770,9 @@ AmmActionCard {
|
||||
return
|
||||
}
|
||||
|
||||
var decimals = AmountMath.implyDecimals(token.totalSupplyRaw || "0")
|
||||
root.tokenResolutionMessage = qsTr("%1 - supply %2 (%3 implied decimals)")
|
||||
root.tokenResolutionMessage = qsTr("%1 - raw supply %2")
|
||||
.arg(token.name || root.shortId(token.definitionId))
|
||||
.arg(AmountMath.formatRaw(token.totalSupplyRaw || "0", decimals))
|
||||
.arg(decimals)
|
||||
.arg(AmountMath.formatRaw(token.totalSupplyRaw || "0", 0))
|
||||
root.selectToken(side, token.definitionId)
|
||||
}
|
||||
|
||||
@ -1193,7 +1191,7 @@ AmmActionCard {
|
||||
"amount_required": qsTr("Enter a value."),
|
||||
"amount_must_be_positive": qsTr("Value must be greater than zero."),
|
||||
"invalid_amount_format": qsTr("Use plain dot-decimal format."),
|
||||
"invalid_amount_precision": qsTr("Too many decimal places for this token."),
|
||||
"invalid_amount_precision": qsTr("Token amounts must use whole raw units."),
|
||||
"invalid_raw_amount": qsTr("Value is outside the supported range."),
|
||||
"amount_exceeds_balance": qsTr("Amount exceeds the selected holding balance."),
|
||||
"amount_too_low": qsTr("Value is too low for this pool."),
|
||||
@ -1239,7 +1237,6 @@ AmmActionCard {
|
||||
|
||||
function finishActiveAmount(side, value) {
|
||||
var decimals = side === "A" ? root.decimalsA : root.decimalsB
|
||||
value = AmountMath.trimHumanPrecision(value, decimals)
|
||||
if (side === "A")
|
||||
root.amountA = value
|
||||
else
|
||||
@ -1302,7 +1299,6 @@ AmmActionCard {
|
||||
|
||||
function finishMissingAmount(side, value) {
|
||||
var decimals = side === "A" ? root.decimalsA : root.decimalsB
|
||||
value = AmountMath.trimHumanPrecision(value, decimals)
|
||||
if (side === "A")
|
||||
root.amountA = value
|
||||
else
|
||||
@ -1483,8 +1479,7 @@ AmmActionCard {
|
||||
}
|
||||
|
||||
function tokenBalanceDetail(token) {
|
||||
var decimals = AmountMath.implyDecimals(token.totalSupplyRaw || "0")
|
||||
return qsTr("Available %1").arg(root.balanceText(token, decimals))
|
||||
return qsTr("Available %1").arg(root.balanceText(token, 0))
|
||||
}
|
||||
|
||||
function shortId(value) {
|
||||
|
||||
@ -59,7 +59,7 @@ TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
function equalDecimalsContext() {
|
||||
function rawAmountsContext() {
|
||||
return {
|
||||
"status": "ready",
|
||||
"tokens": [
|
||||
@ -118,15 +118,23 @@ TestCase {
|
||||
compare(built.request.tokenBId, tokenLow)
|
||||
}
|
||||
|
||||
function test_tokenAmountsUseRawUnits() {
|
||||
var form = createForm()
|
||||
compare(form.decimalsA, 0)
|
||||
compare(form.decimalsB, 0)
|
||||
compare(form.balanceText(form.tokenA, form.decimalsA), "1000")
|
||||
compare(form.balanceText(form.tokenB, form.decimalsB), "5000000000")
|
||||
}
|
||||
|
||||
function test_missingPoolKeepsMinimumRatioWhileEditingDeposit() {
|
||||
var quote = {
|
||||
"status": "ok",
|
||||
"tokenAId": tokenHigh,
|
||||
"tokenBId": tokenLow,
|
||||
"poolStatus": "missing_pool",
|
||||
"minimumAmountARaw": "2000000",
|
||||
"minimumAmountARaw": "2",
|
||||
"minimumAmountBRaw": "3",
|
||||
"actualAmountARaw": "2000000",
|
||||
"actualAmountARaw": "2",
|
||||
"actualAmountBRaw": "3"
|
||||
}
|
||||
var form = createForm()
|
||||
@ -146,14 +154,14 @@ TestCase {
|
||||
|
||||
var built = form.buildQuoteRequest()
|
||||
verify(built.ok)
|
||||
compare(built.request.amountARaw, "4000000")
|
||||
compare(built.request.amountARaw, "4")
|
||||
compare(built.request.amountBRaw, "6")
|
||||
}
|
||||
|
||||
function test_missingPoolAcceptsLargeDirectAmountsFromEitherSide() {
|
||||
var form = createTemporaryObject(formComponent, testCase, {
|
||||
"flowState": flowState(({})),
|
||||
"newPositionContext": equalDecimalsContext()
|
||||
"newPositionContext": rawAmountsContext()
|
||||
})
|
||||
verify(form)
|
||||
wait(0)
|
||||
@ -176,8 +184,8 @@ TestCase {
|
||||
compare(form.amountB, "100")
|
||||
var built = form.buildQuoteRequest()
|
||||
verify(built.ok)
|
||||
compare(built.request.amountARaw, "100000000")
|
||||
compare(built.request.amountBRaw, "150000000")
|
||||
compare(built.request.amountARaw, "100")
|
||||
compare(built.request.amountBRaw, "150")
|
||||
compare(built.request.initialPriceRealRaw, "27670116110564327424")
|
||||
verify(!built.request.hasOwnProperty("depositScaleBps"))
|
||||
|
||||
@ -186,14 +194,14 @@ TestCase {
|
||||
compare(form.amountB, "200")
|
||||
built = form.buildQuoteRequest()
|
||||
verify(built.ok)
|
||||
compare(built.request.amountARaw, "200000000")
|
||||
compare(built.request.amountBRaw, "300000000")
|
||||
compare(built.request.amountARaw, "200")
|
||||
compare(built.request.amountBRaw, "300")
|
||||
}
|
||||
|
||||
function test_missingPoolRoundsPairedAmountAndTrimsInputPrecision() {
|
||||
function test_missingPoolRoundsPairedRawAmounts() {
|
||||
var form = createTemporaryObject(formComponent, testCase, {
|
||||
"flowState": flowState(({})),
|
||||
"newPositionContext": equalDecimalsContext()
|
||||
"newPositionContext": rawAmountsContext()
|
||||
})
|
||||
verify(form)
|
||||
wait(0)
|
||||
@ -204,22 +212,18 @@ TestCase {
|
||||
"tokenAId": tokenHigh,
|
||||
"tokenBId": tokenLow,
|
||||
"poolStatus": "missing_pool",
|
||||
"minimumAmountARaw": "26",
|
||||
"minimumAmountBRaw": "39",
|
||||
"actualAmountARaw": "26",
|
||||
"actualAmountBRaw": "39"
|
||||
"minimumAmountARaw": "1",
|
||||
"minimumAmountBRaw": "1",
|
||||
"actualAmountARaw": "1",
|
||||
"actualAmountBRaw": "1"
|
||||
})
|
||||
wait(0)
|
||||
|
||||
var cases = [
|
||||
{ "input": "1", "paired": "0.666667", "rawA": "666667", "rawB": "1000000",
|
||||
"price": "27670102275513189667" },
|
||||
{ "input": "2", "paired": "1.333333", "rawA": "1333333", "rawB": "2000000",
|
||||
"price": "27670123028095084447" },
|
||||
{ "input": "3", "paired": "2", "rawA": "2000000", "rawB": "3000000",
|
||||
"price": "27670116110564327424" },
|
||||
{ "input": "4", "paired": "2.666667", "rawA": "2666667", "rawB": "4000000",
|
||||
"price": "27670112651800245948" }
|
||||
{ "input": "1", "paired": "1", "rawA": "1", "rawB": "1" },
|
||||
{ "input": "2", "paired": "1", "rawA": "1", "rawB": "2" },
|
||||
{ "input": "3", "paired": "2", "rawA": "2", "rawB": "3" },
|
||||
{ "input": "4", "paired": "3", "rawA": "3", "rawB": "4" }
|
||||
]
|
||||
for (var i = 0; i < cases.length; ++i) {
|
||||
form.finishMissingAmount("A", cases[i].input)
|
||||
@ -228,13 +232,12 @@ TestCase {
|
||||
verify(built.ok)
|
||||
compare(built.request.amountARaw, cases[i].rawA)
|
||||
compare(built.request.amountBRaw, cases[i].rawB)
|
||||
compare(built.request.initialPriceRealRaw, cases[i].price)
|
||||
}
|
||||
|
||||
form.finishMissingAmount("A", "1.1234567")
|
||||
compare(form.amountA, "1.123456")
|
||||
compare(form.amountB, "0.748971")
|
||||
verify(form.buildQuoteRequest().ok)
|
||||
compare(form.amountA, "1.1234567")
|
||||
verify(!form.buildQuoteRequest().ok)
|
||||
compare(form.fieldError("amountA"), form.issueText("invalid_amount_precision"))
|
||||
}
|
||||
|
||||
function test_missingPoolUsesTradeStyleInputsWithInlinePrice() {
|
||||
@ -374,9 +377,9 @@ TestCase {
|
||||
"tokenAId": tokenHigh,
|
||||
"tokenBId": tokenLow,
|
||||
"poolStatus": "active_pool",
|
||||
"reserveARaw": "2000000",
|
||||
"reserveARaw": "2",
|
||||
"reserveBRaw": "10",
|
||||
"maxAmountARaw": "4000000",
|
||||
"maxAmountARaw": "4",
|
||||
"maxAmountBRaw": "20",
|
||||
"errors": [{
|
||||
"code": "amount_exceeds_balance",
|
||||
@ -404,9 +407,9 @@ TestCase {
|
||||
"tokenAId": tokenHigh,
|
||||
"tokenBId": tokenLow,
|
||||
"poolStatus": "active_pool",
|
||||
"reserveARaw": "2000000",
|
||||
"reserveARaw": "2",
|
||||
"reserveBRaw": "10",
|
||||
"maxAmountARaw": "4000000",
|
||||
"maxAmountARaw": "4",
|
||||
"maxAmountBRaw": "20"
|
||||
}
|
||||
var form = createForm()
|
||||
@ -428,11 +431,12 @@ TestCase {
|
||||
|
||||
var built = form.buildQuoteRequest()
|
||||
verify(built.ok)
|
||||
compare(built.request.maxAmountARaw, "1000000")
|
||||
compare(built.request.maxAmountARaw, "1")
|
||||
compare(built.request.maxAmountBRaw, "5")
|
||||
|
||||
form.finishActiveAmount("B", "1.1234567")
|
||||
compare(form.amountB, "1.123456")
|
||||
compare(form.amountB, "1.1234567")
|
||||
verify(!form.buildQuoteRequest().ok)
|
||||
}
|
||||
|
||||
function test_activePoolEditRecoversAfterInvalidQuote() {
|
||||
@ -443,9 +447,9 @@ TestCase {
|
||||
"tokenBId": tokenLow,
|
||||
"poolStatus": "active_pool",
|
||||
"poolFeeBps": 30,
|
||||
"reserveARaw": "2000000",
|
||||
"reserveARaw": "2",
|
||||
"reserveBRaw": "10",
|
||||
"maxAmountARaw": "4000000",
|
||||
"maxAmountARaw": "4",
|
||||
"maxAmountBRaw": "20"
|
||||
})
|
||||
wait(0)
|
||||
@ -466,7 +470,7 @@ TestCase {
|
||||
|
||||
var built = form.buildQuoteRequest()
|
||||
verify(built.ok)
|
||||
compare(built.request.maxAmountARaw, "1000000")
|
||||
compare(built.request.maxAmountARaw, "1")
|
||||
compare(built.request.maxAmountBRaw, "5")
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user