diff --git a/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestAmountLocaleTest.kt b/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestAmountLocaleTest.kt
index 5a54a6b2a3..d7b3503b08 100644
--- a/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestAmountLocaleTest.kt
+++ b/app/src/androidTest/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestAmountLocaleTest.kt
@@ -45,7 +45,7 @@ class PaymentRequestAmountLocaleTest {
}
@Test
- fun commaDecimalEntryPreservesExactCentsAndTrailingZeros() {
+ fun dotDecimalEntryPreservesExactCentsInCommaRegions() {
var submitted: PaykitPaymentRequestDraft? = null
val amountInput = AmountInputViewModel(AmountInputHandler.stub())
composeTestRule.setContent {
@@ -67,15 +67,15 @@ class PaymentRequestAmountLocaleTest {
}
}
- composeTestRule.onNodeWithText(",", useUnmergedTree = true).assertIsDisplayed()
+ composeTestRule.onNodeWithText(".", useUnmergedTree = true).assertIsDisplayed()
listOf("N1", "N5", "N0", "NDecimal").forEach {
composeTestRule.onNodeWithTag(it).performClick()
}
- composeTestRule.onNodeWithText("150,", useUnmergedTree = true).assertIsDisplayed()
+ composeTestRule.onNodeWithText("150.", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithTag("PaymentRequestAmountContinue").assertIsEnabled()
composeTestRule.onNodeWithTag("N5").performClick()
composeTestRule.onNodeWithTag("N0").performClick()
- composeTestRule.onNodeWithText("150,50", useUnmergedTree = true).assertIsDisplayed()
+ composeTestRule.onNodeWithText("150.50", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithTag("PaymentRequestAmountContinue").performClick()
assertEquals(PaykitAmount(PaykitAsset.USD, 15_050uL), submitted?.amount)
diff --git a/app/src/androidTest/java/to/bitkit/ui/screens/wallets/usdt/UsdtPaymentContentTest.kt b/app/src/androidTest/java/to/bitkit/ui/screens/wallets/usdt/UsdtPaymentContentTest.kt
index abefd7e155..b38b94b04a 100644
--- a/app/src/androidTest/java/to/bitkit/ui/screens/wallets/usdt/UsdtPaymentContentTest.kt
+++ b/app/src/androidTest/java/to/bitkit/ui/screens/wallets/usdt/UsdtPaymentContentTest.kt
@@ -79,7 +79,7 @@ class UsdtPaymentContentTest {
}
@Test
- fun decimalKeyMatchesAmountLocaleWithoutChangingInputEncoding() {
+ fun decimalKeyUsesDotInCommaRegionsWithoutChangingInputEncoding() {
Locale.setDefault(Locale.GERMANY)
val amount = mutableStateOf("5")
composeTestRule.setContent {
@@ -97,13 +97,39 @@ class UsdtPaymentContentTest {
}
}
}
- composeTestRule.onNodeWithText(",").assertIsDisplayed()
+ composeTestRule.onNodeWithText(".").assertIsDisplayed()
composeTestRule.onNodeWithTag("NDecimal").performClick()
composeTestRule.onNodeWithTag("N5").performClick()
- composeTestRule.onNodeWithText("5,5").assertIsDisplayed()
+ composeTestRule.onNodeWithText("5.5").assertIsDisplayed()
composeTestRule.runOnIdle { assertEquals("5.5", amount.value) }
}
+ @Test
+ fun walletDistinguishesUnknownAndConfirmedBalancesSilentlyAndKeepsPrivacy() {
+ Locale.setDefault(Locale.GERMANY)
+ val wallet = mutableStateOf(UsdtWalletState())
+ val hidden = mutableStateOf(false)
+ composeTestRule.setContent {
+ AppThemeSurface {
+ UsdtWalletContent(
+ wallet = wallet.value,
+ hideBalance = hidden.value,
+ onReceive = {}, onSend = {}, onBack = {},
+ )
+ }
+ }
+ composeTestRule.onNodeWithText("—").assertIsDisplayed()
+ composeTestRule.onNodeWithText("0.00").assertDoesNotExist()
+ composeTestRule.onNodeWithTag("UsdtHistorySync").assertDoesNotExist()
+ composeTestRule.runOnIdle { wallet.value = wallet.value.copy(balance = 0uL) }
+ composeTestRule.onNodeWithText("0.00").assertIsDisplayed()
+ composeTestRule.onNodeWithText("—").assertDoesNotExist()
+ composeTestRule.runOnIdle { wallet.value = wallet.value.copy(balance = 1_234_567_890uL) }
+ composeTestRule.onNodeWithText("1,234.57").assertIsDisplayed()
+ composeTestRule.runOnIdle { hidden.value = true }
+ composeTestRule.onNodeWithText("1,234.57").assertDoesNotExist()
+ }
+
@Test
fun largeHeadersAndNetworkLabelsRemainReadable() {
composeTestRule.setContent {
diff --git a/app/src/main/java/to/bitkit/models/Currency.kt b/app/src/main/java/to/bitkit/models/Currency.kt
index c74930db7e..0f47af0bc1 100644
--- a/app/src/main/java/to/bitkit/models/Currency.kt
+++ b/app/src/main/java/to/bitkit/models/Currency.kt
@@ -139,7 +139,12 @@ fun Long.formatToClassicDisplay(locale: Locale = Locale.getDefault()): String {
return formatter.format(asBtc())
}
-fun BigDecimal.formatCurrency(decimalPlaces: Int = FIAT_DECIMALS, locale: Locale = Locale.getDefault()): String? {
+fun BigDecimal.formatCurrency(
+ decimalPlaces: Int = FIAT_DECIMALS,
+ locale: Locale = Locale.getDefault(),
+ minimumFractionDigits: Int = decimalPlaces,
+ roundingMode: RoundingMode = RoundingMode.HALF_EVEN,
+): String? {
val symbols = DecimalFormatSymbols(locale).apply {
decimalSeparator = DECIMAL_SEPARATOR
groupingSeparator = FIAT_GROUPING_SEPARATOR
@@ -147,8 +152,9 @@ fun BigDecimal.formatCurrency(decimalPlaces: Int = FIAT_DECIMALS, locale: Locale
val decimalPlacesString = "0".repeat(decimalPlaces)
val formatter = DecimalFormat("#,##0.$decimalPlacesString", symbols).apply {
- minimumFractionDigits = decimalPlaces
+ this.minimumFractionDigits = minimumFractionDigits
maximumFractionDigits = decimalPlaces
+ this.roundingMode = roundingMode
isGroupingUsed = true
}
diff --git a/app/src/main/java/to/bitkit/ui/components/PaykitAmountView.kt b/app/src/main/java/to/bitkit/ui/components/PaykitAmountView.kt
index 7623cd1de5..0871457dbc 100644
--- a/app/src/main/java/to/bitkit/ui/components/PaykitAmountView.kt
+++ b/app/src/main/java/to/bitkit/ui/components/PaykitAmountView.kt
@@ -8,9 +8,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import to.bitkit.models.PaykitAmount
import to.bitkit.models.PaykitAsset
+import to.bitkit.models.formatCurrency
import to.bitkit.ui.theme.Colors
import java.math.BigDecimal
-import java.text.NumberFormat
import java.util.Locale
@Composable
@@ -69,10 +69,6 @@ fun PaykitAmountCell(
}
}
-internal fun usdDisplayAmount(cents: ULong, locale: Locale = Locale.getDefault()): String {
- val format = NumberFormat.getNumberInstance(locale).apply {
- minimumFractionDigits = PaykitAsset.USD.decimals
- maximumFractionDigits = PaykitAsset.USD.decimals
- }
- return format.format(BigDecimal(cents.toString()).movePointLeft(PaykitAsset.USD.decimals))
-}
+internal fun usdDisplayAmount(cents: ULong, locale: Locale = Locale.getDefault()): String = checkNotNull(
+ BigDecimal(cents.toString()).movePointLeft(PaykitAsset.USD.decimals).formatCurrency(locale = locale)
+)
diff --git a/app/src/main/java/to/bitkit/ui/components/UsdtAmountHeader.kt b/app/src/main/java/to/bitkit/ui/components/UsdtAmountHeader.kt
index aa306ca4be..1f0f6df6cd 100644
--- a/app/src/main/java/to/bitkit/ui/components/UsdtAmountHeader.kt
+++ b/app/src/main/java/to/bitkit/ui/components/UsdtAmountHeader.kt
@@ -8,13 +8,12 @@ import to.bitkit.ext.nowMillis
import to.bitkit.models.BITCOIN_SYMBOL
import to.bitkit.models.PaykitAsset
import to.bitkit.models.PaykitExchangeRate
+import to.bitkit.models.formatCurrency
import to.bitkit.models.formatMoney
import to.bitkit.repositories.paykitRate
import to.bitkit.ui.LocalCurrencies
import java.math.BigDecimal
import java.math.RoundingMode
-import java.text.DecimalFormatSymbols
-import java.text.NumberFormat
import java.util.Locale
@Composable
@@ -34,7 +33,7 @@ internal fun UsdtAmountHeader(
isBitcoinPrimary = false,
smallRowText = btc ?: "USDT",
smallRowSymbol = if (btc != null) BITCOIN_SYMBOL else null,
- largeRowText = displayAmount ?: usdtInputAmount(amount),
+ largeRowText = displayAmount ?: amount,
largeRowSymbol = "$",
largeRowPrefix = prefix,
showSymbol = true,
@@ -46,26 +45,22 @@ internal fun UsdtAmountHeader(
)
}
-internal fun usdtInputAmount(amount: String, locale: Locale = Locale.getDefault()): String =
- amount.replace('.', DecimalFormatSymbols.getInstance(locale).decimalSeparator)
-
private const val USDT_CENT = 10_000uL
internal fun usdtOverviewAmount(amount: ULong, locale: Locale = Locale.getDefault()): String {
- val format = NumberFormat.getNumberInstance(locale).apply {
- minimumFractionDigits = 2
- maximumFractionDigits = 2
- roundingMode = RoundingMode.HALF_UP
+ if (amount > 0uL && amount < USDT_CENT) {
+ return "<" + checkNotNull(BigDecimal("0.01").formatCurrency(locale = locale))
}
- if (amount > 0uL && amount < USDT_CENT) return "<" + format.format(BigDecimal("0.01"))
- return format.format(BigDecimal(amount.toString()).movePointLeft(PaykitAsset.USDT.decimals))
+ return checkNotNull(
+ BigDecimal(amount.toString()).movePointLeft(PaykitAsset.USDT.decimals)
+ .formatCurrency(locale = locale, roundingMode = RoundingMode.HALF_UP)
+ )
}
-internal fun usdtExactAmount(amount: ULong, locale: Locale = Locale.getDefault()): String =
- NumberFormat.getNumberInstance(locale).apply {
- minimumFractionDigits = 2
- maximumFractionDigits = PaykitAsset.USDT.decimals
- }.format(BigDecimal(amount.toString()).movePointLeft(PaykitAsset.USDT.decimals))
+internal fun usdtExactAmount(amount: ULong, locale: Locale = Locale.getDefault()): String = checkNotNull(
+ BigDecimal(amount.toString()).movePointLeft(PaykitAsset.USDT.decimals)
+ .formatCurrency(decimalPlaces = PaykitAsset.USDT.decimals, locale = locale, minimumFractionDigits = 2)
+)
internal fun usdtDisplaySats(amount: String, rate: PaykitExchangeRate?, now: Long = nowMillis()): Long? {
val dollars = amount.toBigDecimalOrNull()?.takeIf { it.signum() >= 0 } ?: return null
diff --git a/app/src/main/java/to/bitkit/ui/screens/paymentrequests/CreatePaymentRequestScreen.kt b/app/src/main/java/to/bitkit/ui/screens/paymentrequests/CreatePaymentRequestScreen.kt
index 6a5bc97dce..bb58556ba5 100644
--- a/app/src/main/java/to/bitkit/ui/screens/paymentrequests/CreatePaymentRequestScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/paymentrequests/CreatePaymentRequestScreen.kt
@@ -34,7 +34,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
@@ -95,7 +94,6 @@ import to.bitkit.ui.utils.NumberPadInputHandler
import to.bitkit.ui.utils.withAccent
import to.bitkit.viewmodels.AmountInputViewModel
import to.bitkit.viewmodels.AppViewModel
-import java.text.DecimalFormatSymbols
import kotlin.math.abs
import kotlin.time.Clock
import kotlin.time.Duration
@@ -150,12 +148,16 @@ internal fun PaymentRequestAmountContent(
) {
val currencies = LocalCurrencies.current
val amountState by amountInputViewModel.uiState.collectAsStateWithLifecycle()
- val locale = LocalConfiguration.current.locales[0]
- val decimalSeparator = remember(locale) { DecimalFormatSymbols.getInstance(locale).decimalSeparator }
var asset by remember(initialDraft.amount.asset) { mutableStateOf(initialDraft.amount.asset) }
var dollars by remember(initialDraft.amount) {
- mutableStateOf(if (initialDraft.amount.asset != PaykitAsset.BTC) initialDraft.amount.inputValue else "")
+ mutableStateOf(
+ if (initialDraft.amount.asset != PaykitAsset.BTC && initialDraft.amount.atomic != 0uL) {
+ initialDraft.amount.inputValue
+ } else {
+ ""
+ }
+ )
}
var conversionError by remember { mutableStateOf(false) }
val entered = if (asset == PaykitAsset.BTC) {
@@ -204,7 +206,7 @@ internal fun PaymentRequestAmountContent(
)
} else {
NumberPadAmountText(
- value = dollars.ifEmpty { "0" }.replace('.', decimalSeparator),
+ value = dollars.ifEmpty { "0" },
symbol = "$",
modifier = Modifier.testTag("PaymentRequestAmountField")
)
@@ -261,7 +263,6 @@ internal fun PaymentRequestAmountContent(
},
type = NumberPadType.DECIMAL,
availableHeight = availableHeight,
- decimalSeparator = decimalSeparator.toString(),
modifier = Modifier.testTag("PaymentRequestNumberPad")
)
}
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
index b998c1388c..578aeb58c2 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt
@@ -106,7 +106,6 @@ import to.bitkit.viewmodels.SendFundingSource
import to.bitkit.viewmodels.SendMethod
import to.bitkit.viewmodels.SendUiState
import to.bitkit.viewmodels.previewAmountInputViewModel
-import java.text.DecimalFormatSymbols
/** Opacity of unavailable payment sources in the balance selector. */
private const val DISABLED_FUNDING_SOURCE_ALPHA = 0.30f
@@ -426,7 +425,6 @@ private fun SendAmountNodeRunning(
if (uiState.paykitUsesUsdt) {
NumberPad(
type = NumberPadType.DECIMAL,
- decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(),
availableHeight = maxHeight,
enabled = uiState.isAmountEditable,
onPress = {
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositReceiveScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositReceiveScreen.kt
index 8562cdd6bb..31beddc78f 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositReceiveScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositReceiveScreen.kt
@@ -49,6 +49,7 @@ import to.bitkit.ui.components.PrimaryButton
import to.bitkit.ui.components.SendCell
import to.bitkit.ui.components.UsdtAmountHeader
import to.bitkit.ui.components.VerticalSpacer
+import to.bitkit.ui.components.usdDisplayAmount
import to.bitkit.ui.components.usdtOverviewAmount
import to.bitkit.ui.scaffold.SheetTopBar
import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing
@@ -56,8 +57,6 @@ import to.bitkit.ui.screens.wallets.receive.ReceiveTab
import to.bitkit.ui.shared.modifiers.swipeToChangeTab
import to.bitkit.ui.theme.AppTextStyles
import to.bitkit.ui.theme.Colors
-import java.text.DecimalFormatSymbols
-import java.text.NumberFormat
import java.util.Locale
@Composable
@@ -316,8 +315,7 @@ internal val UsdtDepositNetwork.label: String
internal fun String.depositNetwork(): UsdtDepositNetwork? =
UsdtDepositNetwork.entries.firstOrNull { it.name.equals(this, ignoreCase = true) }
internal fun String.usdCents(locale: Locale = Locale.getDefault()): String = toULongOrNull()?.let {
- val whole = NumberFormat.getIntegerInstance(locale).format((it / 100u).toLong())
- "$$whole${DecimalFormatSymbols.getInstance(locale).decimalSeparator}${(it % 100u).toString().padStart(2, '0')}"
+ "$" + usdDisplayAmount(it, locale)
} ?: "—"
internal fun depositStatus(status: String, code: String?): Int = when (status) {
"completed" -> R.string.usdt__confirmed
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtReceiveAmountContent.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtReceiveAmountContent.kt
index a9e70c7e34..2ae1e189b1 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtReceiveAmountContent.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtReceiveAmountContent.kt
@@ -27,7 +27,6 @@ import to.bitkit.ui.components.UsdtAmountHeader
import to.bitkit.ui.components.VerticalSpacer
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.NumberPadInputHandler
-import java.text.DecimalFormatSymbols
@Composable
internal fun ColumnScope.UsdtReceiveAmountContent(
@@ -50,7 +49,6 @@ internal fun ColumnScope.UsdtReceiveAmountContent(
BoxWithConstraints(contentAlignment = Alignment.BottomCenter, modifier = Modifier.weight(1f).fillMaxWidth()) {
NumberPad(
type = NumberPadType.DECIMAL,
- decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(),
availableHeight = maxHeight,
enabled = !busy,
onDeleteLongPress = { onAmount("") },
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt
index 5a70c1ef00..a77106bc13 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt
@@ -97,7 +97,6 @@ import to.bitkit.ui.theme.AppTextStyles
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.NumberPadInputHandler
-import java.text.DecimalFormatSymbols
import kotlin.time.Duration.Companion.seconds
@Composable
@@ -510,7 +509,6 @@ private fun UsdtAmountContent(
HorizontalDivider(color = Colors.White10)
NumberPad(
type = NumberPadType.DECIMAL,
- decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(),
availableHeight = maxHeight,
enabled = !state.busy && amountEditable,
onDeleteLongPress = { onAmountChange("") },
diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtWalletScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtWalletScreen.kt
index 6d30faeb66..07f16c65a1 100644
--- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtWalletScreen.kt
+++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtWalletScreen.kt
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
@@ -60,7 +59,6 @@ import to.bitkit.repositories.UsdtWalletState
import to.bitkit.ui.appViewModel
import to.bitkit.ui.components.BiometricsView
import to.bitkit.ui.components.BodyMSB
-import to.bitkit.ui.components.BodyS
import to.bitkit.ui.components.BottomSheet
import to.bitkit.ui.components.Caption13Up
import to.bitkit.ui.components.Sheet
@@ -105,9 +103,7 @@ fun RowScope.UsdtWalletBalance(onClick: () -> Unit, viewModel: UsdtViewModel = h
if (settings.hideBalance) {
UiConstants.HIDE_BALANCE_SHORT
} else {
- wallet.balance?.let {
- usdtOverviewAmount(it)
- } ?: "—"
+ wallet.balance?.let { "$ " + usdtOverviewAmount(it) } ?: "—"
},
style = UsdtDetailStyles.HistoryTitle
)
@@ -184,7 +180,6 @@ fun UsdtWalletScreen(
} else {
UsdtWalletContent(
wallet = wallet,
- error = state.refreshError,
hideBalance = settings.hideBalance,
onReceive = {
app?.requestUsdtEnable {
@@ -317,9 +312,8 @@ internal fun rememberUsdtPage(initialRecipient: String, state: UsdtSendState): M
}
@Composable
-private fun UsdtWalletContent(
+internal fun UsdtWalletContent(
wallet: UsdtWalletState,
- error: Int?,
hideBalance: Boolean,
onReceive: () -> Unit,
onSend: () -> Unit,
@@ -355,10 +349,6 @@ private fun UsdtWalletContent(
modifier = Modifier.testTag("UsdtBalance")
)
VerticalSpacer(32.dp)
- error?.let {
- BodyS(stringResource(it), color = Colors.Brand)
- VerticalSpacer(16.dp)
- }
}
itemsIndexed(wallet.transfers, key = { _, transfer -> transfer.id }) { index, transfer ->
val heading = activityGroupTitleResource(transfer.timestamp)
@@ -373,13 +363,6 @@ private fun UsdtWalletContent(
VerticalSpacer(16.dp)
}
item {
- if (!wallet.historyComplete) {
- BodyS(
- stringResource(R.string.usdt__history_sync),
- color = Colors.White64,
- modifier = Modifier.fillMaxWidth().background(Colors.Black).testTag("UsdtHistorySync")
- )
- }
VerticalSpacer(120.dp)
}
}
@@ -483,6 +466,6 @@ internal enum class UsdtPage {
@Composable
private fun Preview() {
AppThemeSurface {
- UsdtWalletContent(UsdtWalletState(balance = 100_000_000u), null, false, {}, {}, {})
+ UsdtWalletContent(UsdtWalletState(balance = 100_000_000u), false, {}, {}, {})
}
}
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 6d30699ed9..75e9afbf60 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -817,7 +817,6 @@
تُدفع جميع الرسوم بـ USDT. قد تكون الرسوم النهائية أقل.
من العنوان
إخفاء التفاصيل
- لا تزال مزامنة النشاط الأقدم جارية. رصيدك محدَّث عند توفر الاتصال.
لا، شكرًا
Bitcoin في الصميم. ودولارات عندما تحتاجها. فعّل USDT (Tether) لإرسال الدولارات الرقمية واستلامها باستخدام Bitkit.
<accent>دولارات؟</accent>\nالقرار لك
diff --git a/app/src/main/res/values-b+es+419/strings.xml b/app/src/main/res/values-b+es+419/strings.xml
index be92ca0479..d3bb161040 100644
--- a/app/src/main/res/values-b+es+419/strings.xml
+++ b/app/src/main/res/values-b+es+419/strings.xml
@@ -834,7 +834,6 @@
Todas las comisiones se pagan en USDT. La comisión final puede ser menor.
Origen
Ocultar detalles
- La actividad anterior aún se está sincronizando. Su saldo estará actualizado cuando haya conexión.
No, gracias
Bitcoin en esencia. Dólares cuando los necesite. Active USDT (Tether) para enviar y recibir dólares digitales con Bitkit.
<accent>¿DÓLARES?</accent>\nUSTED DECIDE
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index d454c6b474..9f08464d85 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -827,7 +827,6 @@
Totes les tarifes es paguen en USDT. La tarifa final pot ser inferior.
Adreça d\'origen
Amagar detalls
- L\'activitat anterior encara s\'està sincronitzant. El teu saldo està al dia quan hi ha connexió.
No, gràcies
Bitcoin de cor. Dòlars quan els necessitis. Activa USDT (Tether) per enviar i rebre dòlars digitals amb Bitkit.
<accent>DÒLARS?</accent>\nTU DECIDEIXES
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 12163a1f2d..ea476e8e0e 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -831,7 +831,6 @@
Všechny poplatky se platí v USDT. Konečný poplatek může být nižší.
Z adresy
Skrýt podrobnosti
- Starší aktivita se stále synchronizuje. Když je připojení k dispozici, je váš zůstatek aktuální.
Ne, děkuji
V srdci bitcoin. Dolary, když je potřebujete. Zapněte USDT (Tether) a posílejte a přijímejte digitální dolary s Bitkit.
<accent>DOLARY?</accent>\nVAŠE VOLBA
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 6ba9bcbeff..094637b620 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -829,7 +829,6 @@
Alle Gebühren werden in USDT bezahlt. Die endgültige Gebühr kann niedriger sein.
Absenderadresse
Details ausblenden
- Ältere Aktivitäten werden noch synchronisiert. Dein Guthaben ist aktuell, sobald eine Verbindung besteht.
Nein danke
Im Herzen Bitcoin. Dollar, wenn du sie brauchst. Aktiviere USDT (Tether), um mit Bitkit digitale Dollar zu senden und zu empfangen.
<accent>DOLLAR?</accent>\nDU ENTSCHEIDEST
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index e59576a82a..ad210421e7 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -819,7 +819,6 @@
Όλα τα τέλη πληρώνονται σε USDT. Το τελικό τέλος μπορεί να είναι χαμηλότερο.
Από διεύθυνση
Απόκρυψη λεπτομερειών
- Η παλαιότερη δραστηριότητα συγχρονίζεται ακόμα. Το υπόλοιπό σου είναι ενημερωμένο όταν υπάρχει σύνδεση.
Όχι, ευχαριστώ
Bitcoin στην καρδιά. Δολάρια όταν τα χρειάζεσαι. Ενεργοποίησε το USDT (Tether) για να στέλνεις και να λαμβάνεις ψηφιακά δολάρια με το Bitkit.
<accent>ΔΟΛΑΡΙΑ;</accent>\nΔΙΚΗ ΣΟΥ ΕΠΙΛΟΓΗ
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 3c464fa803..8660c794d7 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -830,7 +830,6 @@
Todas las comisiones se pagan en USDT. La comisión final puede ser menor.
Origen
Ocultar detalles
- La actividad anterior aún se está sincronizando. Tu saldo estará actualizado cuando haya conexión.
No, gracias
Bitcoin en esencia. Dólares cuando los necesites. Activa USDT (Tether) para enviar y recibir dólares digitales con Bitkit.
<accent>¿DÓLARES?</accent>\nTÚ DECIDES
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 215c021b3c..ae62e8b85a 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -827,7 +827,6 @@
Todas las comisiones se pagan en USDT. La comisión final puede ser menor.
Origen
Ocultar detalles
- La actividad anterior aún se está sincronizando. Su saldo estará actualizado cuando haya conexión.
No, gracias
Bitcoin en esencia. Dólares cuando los necesite. Active USDT (Tether) para enviar y recibir dólares digitales con Bitkit.
<accent>¿DÓLARES?</accent>\nUSTED DECIDE
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 3bff9a28e1..db8bb38718 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -831,7 +831,6 @@
Tous les frais sont payés en USDT. Les frais finaux peuvent être inférieurs.
Adresse d\'envoi
Masquer les détails
- L\'activité plus ancienne est encore en cours de synchronisation. Votre solde est à jour lorsque la connexion est disponible.
Non merci
Bitcoin dans l\'âme. Des dollars quand vous en avez besoin. Activez USDT (Tether) pour envoyer et recevoir des dollars numériques avec Bitkit.
<accent>DES DOLLARS ?</accent>\nÀ VOUS DE VOIR
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0b99b9d7dd..cdec208e62 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -830,7 +830,6 @@
Tutte le commissioni sono pagate in USDT. La commissione finale potrebbe essere inferiore.
Indirizzo mittente
Nascondi dettagli
- Le attività meno recenti sono ancora in sincronizzazione. Il tuo saldo è aggiornato quando la connessione è disponibile.
No Grazie
Bitcoin nel cuore. Dollari quando ti servono. Attiva USDT (Tether) per inviare e ricevere dollari digitali con Bitkit.
<accent>DOLLARI?</accent>\nDECIDI TU
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 02d12a586d..739d03b6ee 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -826,7 +826,6 @@
Alle vergoedingen worden in USDT betaald. De uiteindelijke vergoeding kan lager zijn.
Van adres
Details verbergen
- Oudere activiteit wordt nog gesynchroniseerd. Uw saldo is actueel zodra er verbinding is.
Nee, bedankt
Bitcoin in hart en nieren. Dollars wanneer u ze nodig heeft. Schakel USDT (Tether) in om digitale dollars te verzenden en ontvangen met Bitkit.
<accent>DOLLARS?</accent>\nU BESLIST
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index af79197cb0..7442641593 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -833,7 +833,6 @@
Wszystkie opłaty są płacone w USDT. Ostateczna opłata może być niższa.
Z adresu
Ukryj szczegóły
- Starsza aktywność wciąż się synchronizuje. Saldo jest aktualne, gdy połączenie jest dostępne.
Nie, dzięki
Bitcoin w sercu. Dolary, gdy ich potrzebujesz. Włącz USDT (Tether), aby wysyłać i otrzymywać cyfrowe dolary w Bitkit.
<accent>DOLARY?</accent>\nTWÓJ WYBÓR
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index c67faa0a75..c4539da614 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -834,7 +834,6 @@
Todas as taxas são pagas em USDT. A taxa final pode ser menor.
Endereço de origem
Ocultar detalhes
- A atividade mais antiga ainda está sincronizando. Seu saldo fica atualizado sempre que houver conexão.
Não, Obrigado
Bitcoin no coração. Dólares quando você precisar. Ative o USDT (Tether) para enviar e receber dólares digitais com a Bitkit.
<accent>DÓLARES?</accent>\nVOCÊ DECIDE
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 1d94fe96b1..61ef6e1fd4 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1041,7 +1041,6 @@
Todas as taxas são pagas em USDT. A taxa final pode ser inferior.
Endereço de origem
Ocultar detalhes
- A atividade mais antiga ainda está a sincronizar. O seu saldo está atualizado sempre que houver ligação.
Não, obrigado
Bitcoin no coração. Dólares quando precisar. Ative o USDT (Tether) para enviar e receber dólares digitais com a Bitkit.
<accent>DÓLARES?</accent>\nA ESCOLHA É SUA
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index b2c5b09f55..e69a0cb169 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -852,7 +852,6 @@
Все комиссии оплачиваются в USDT. Итоговая комиссия может быть ниже.
С адреса
Скрыть детали
- Более ранняя активность ещё синхронизируется. Ваш баланс актуален, когда есть подключение.
Нет, Спасибо
Биткоин в душе. Доллары, когда они нужны. Включите USDT (Tether), чтобы отправлять и получать цифровые доллары в Bitkit.
<accent>ДОЛЛАРЫ?</accent>\nРЕШАТЬ ВАМ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8187e20fbc..83d11a9895 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1237,7 +1237,6 @@
All fees are paid in USDT. Final fee may be lower.
From address
Hide details
- Older activity is still syncing. Your balance is up to date when the connection is available.
No Thanks
Bitcoin at heart. Dollars when you need them. Enable USDT (Tether) to send and receive digital dollars with Bitkit.
<accent>DOLLARS?</accent>\nYOUR CALL
diff --git a/app/src/test/java/to/bitkit/ui/components/PaykitAmountViewTest.kt b/app/src/test/java/to/bitkit/ui/components/PaykitAmountViewTest.kt
index af1b23370c..e1ace12947 100644
--- a/app/src/test/java/to/bitkit/ui/components/PaykitAmountViewTest.kt
+++ b/app/src/test/java/to/bitkit/ui/components/PaykitAmountViewTest.kt
@@ -1,22 +1,29 @@
package to.bitkit.ui.components
import org.junit.Test
+import to.bitkit.models.formatCurrency
+import java.math.BigDecimal
import java.util.Locale
import kotlin.test.assertEquals
class PaykitAmountViewTest {
@Test
- fun `usd display preserves cents and uses locale grouping`() {
+ fun `usd display preserves cents and matches fiat separators across regions`() {
listOf(
- Triple(0uL, "0.00", "0,00"),
- Triple(1uL, "0.01", "0,01"),
- Triple(100uL, "1.00", "1,00"),
- Triple(15050uL, "150.50", "150,50"),
- Triple(123456uL, "1,234.56", "1.234,56"),
- Triple(ULong.MAX_VALUE, "184,467,440,737,095,516.15", "184.467.440.737.095.516,15"),
- ).forEach { (cents, us, de) ->
- assertEquals(us, usdDisplayAmount(cents, Locale.US), "cents: $cents")
- assertEquals(de, usdDisplayAmount(cents, Locale.GERMANY), "cents: $cents")
+ 0uL to "0.00",
+ 1uL to "0.01",
+ 100uL to "1.00",
+ 15050uL to "150.50",
+ 123456uL to "1,234.56",
+ ULong.MAX_VALUE to "184,467,440,737,095,516.15",
+ ).forEach { (cents, expected) ->
+ listOf(Locale.US, Locale.GERMANY, Locale.forLanguageTag("nl-NL"), Locale.FRANCE).forEach { locale ->
+ assertEquals(expected, usdDisplayAmount(cents, locale), locale.toString())
+ assertEquals(
+ BigDecimal(cents.toString()).movePointLeft(2).formatCurrency(locale = locale),
+ usdDisplayAmount(cents, locale),
+ )
+ }
}
}
}
diff --git a/app/src/test/java/to/bitkit/ui/components/UsdtDisplayTest.kt b/app/src/test/java/to/bitkit/ui/components/UsdtDisplayTest.kt
index 04a3958d14..49f02ce6cc 100644
--- a/app/src/test/java/to/bitkit/ui/components/UsdtDisplayTest.kt
+++ b/app/src/test/java/to/bitkit/ui/components/UsdtDisplayTest.kt
@@ -8,18 +8,19 @@ import kotlin.test.assertNull
class UsdtDisplayTest {
@Test
- fun `amount input uses the local separator and preserves precision and trailing zeros`() {
- assertEquals("150,50", usdtInputAmount("150.50", Locale.GERMANY))
- assertEquals("0,000001", usdtInputAmount("0.000001", Locale.GERMANY))
- assertEquals("150.50", usdtInputAmount("150.50", Locale.US))
- assertEquals("1,", usdtInputAmount("1.", Locale.GERMANY))
- }
-
- @Test
- fun `exact amounts preserve token precision and locale separators`() {
- assertEquals("4.996", usdtExactAmount(4_996_000u, Locale.US))
- assertEquals("1,234567", usdtExactAmount(1_234_567u, Locale.GERMANY))
- assertEquals("3,50", usdtExactAmount(3_500_000u, Locale.GERMANY))
+ fun `exact amounts preserve token precision across regions`() {
+ listOf(Locale.US, Locale.GERMANY, Locale.forLanguageTag("nl-NL"), Locale.FRANCE).forEach { locale ->
+ listOf(
+ 0uL to "0.00",
+ 1uL to "0.000001",
+ 4_996_000uL to "4.996",
+ 1_234_567uL to "1.234567",
+ 3_500_000uL to "3.50",
+ ULong.MAX_VALUE to "18,446,744,073,709.551615",
+ ).forEach { (amount, expected) ->
+ assertEquals(expected, usdtExactAmount(amount, locale), locale.toString())
+ }
+ }
}
@Test
@@ -30,6 +31,7 @@ class UsdtDisplayTest {
assertEquals(0L, usdtDisplaySats("0.000001", rate, now))
assertNull(usdtDisplaySats("3.5", null, now))
assertNull(usdtDisplaySats("3.5", rate.copy(timestampMillis = now - 601_000), now))
+ assertNull(usdtDisplaySats("—", rate, now))
assertNull(usdtDisplaySats("-1", rate, now))
assertNull(usdtDisplaySats("999999999999999999999999", rate, now))
}
@@ -46,9 +48,9 @@ class UsdtDisplayTest {
1025000uL to "1.03",
1234567890uL to "1,234.57",
).forEach { (amount, expected) ->
- assertEquals(expected, usdtOverviewAmount(amount, Locale.US), "amount: $amount")
+ listOf(Locale.US, Locale.GERMANY, Locale.forLanguageTag("nl-NL"), Locale.FRANCE).forEach { locale ->
+ assertEquals(expected, usdtOverviewAmount(amount, locale), locale.toString())
+ }
}
- assertEquals("<0,01", usdtOverviewAmount(1uL, Locale.GERMANY))
- assertEquals("1.234,57", usdtOverviewAmount(1234567890uL, Locale.GERMANY))
}
}
diff --git a/app/src/test/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositViewModelTest.kt b/app/src/test/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositViewModelTest.kt
index 488ea77a9c..83499766d0 100644
--- a/app/src/test/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositViewModelTest.kt
+++ b/app/src/test/java/to/bitkit/ui/screens/wallets/usdt/UsdtDepositViewModelTest.kt
@@ -56,8 +56,8 @@ class UsdtDepositViewModelTest : BaseUnitTest() {
@Test
fun `USD cents respect locale grouping and decimal separators`() {
assertEquals("$10,000.00", "1000000".usdCents(Locale.US))
- assertEquals("$10.000,00", "1000000".usdCents(Locale.GERMANY))
- assertEquals("$0,05", "5".usdCents(Locale.GERMANY))
+ assertEquals("$10,000.00", "1000000".usdCents(Locale.GERMANY))
+ assertEquals("$0.05", "5".usdCents(Locale.GERMANY))
}
@Test
diff --git a/changelog.d/next/1655.fixed.md b/changelog.d/next/1655.fixed.md
new file mode 100644
index 0000000000..3b3e2a9794
--- /dev/null
+++ b/changelog.d/next/1655.fixed.md
@@ -0,0 +1 @@
+Show USDT balances quietly with consistent decimals, a currency symbol on Home and an unknown marker until the balance loads.
diff --git a/journeys/README.md b/journeys/README.md
index 059e46249e..4cc564cfb9 100644
--- a/journeys/README.md
+++ b/journeys/README.md
@@ -123,7 +123,7 @@ journey PR, which is what made this file conflict on every merge.
| Capability | Provided by |
| --- | --- |
-| USDT wallet navigation without funding | an onboarded build with USDT configured; provider errors may remain visible while offline — [USDT QA](../docs/usdt-qa.md) |
+| USDT wallet navigation without funding | an onboarded build with USDT configured; wallet syncing remains silent while offline; payment-action errors remain available — [USDT QA](../docs/usdt-qa.md) |
| Controlled USDT quotes, provider history, funded submission and recovery | an explicitly configured local USDT gateway/RPC/bundler fixture with owned test accounts and funds; absent from the standard journey environment. Report these steps as blocked unless that setup and transfer authorization are supplied — [USDT QA](../docs/usdt-qa.md) |
| On-chain funds and blocks on regtest | `./lsp` deposit and mine against the staging LSP — [Backend preconditions](#backend-preconditions) |
| Several separate on-chain UTXOs to choose between | three or more `./lsp` deposits, each mined, so manual coin selection has inputs to list — [Backend preconditions](#backend-preconditions) |
diff --git a/journeys/usdt-bridge-details.xml b/journeys/usdt-bridge-details.xml
index f153e2ccb5..8f77ea64c1 100644
--- a/journeys/usdt-bridge-details.xml
+++ b/journeys/usdt-bridge-details.xml
@@ -5,7 +5,7 @@
Tap Details (id "UsdtActivityDetails"). Verify the real wallet transaction and available addresses are shown without inventing a provider deposit reference or refund eligibility.
Open Cross-Network Deposits (id "UsdtDepositHistory"). Verify the Provider Status note says the spendable balance updates when USDT arrives on Arbitrum, and each row shows its source network, provider status and available amount.
Open a supported deposit that needs attention (id "UsdtDepositRow-{depositId}"). Verify the summary appears first with the provider status and receiving network. Do not invent a deposit date or individual fee when the provider does not supply one. Tap Details (id "UsdtDepositDetails"). Verify its own source transaction and deposit reference are shown. Verify the refund instructions and address placeholder name that deposit's source network.
- Enter an owned test refund address (id "UsdtRefundAddress") slowly across two background refreshes. Verify the field stays enabled and focused, the keyboard remains open, and every typed character is retained while the provider status updates. Then tap Request refund (id "UsdtDepositRefund"). Verify Confirm Refund shows the exact deposit amount with up to six decimals, network, full refund address and refund fee note. In a comma locale, a 3.5 USDT deposit must display 3,50 USDT. Hidden balances must remain masked, and an unknown amount must remain a dash. Cancel without submitting.
+ Enter an owned test refund address (id "UsdtRefundAddress") slowly across two background refreshes. Verify the field stays enabled and focused, the keyboard remains open, and every typed character is retained while the provider status updates. Then tap Request refund (id "UsdtDepositRefund"). Verify Confirm Refund shows the exact deposit amount with up to six decimals, network, full refund address and refund fee note. In a comma locale, a 3.5 USDT deposit must display 3.50 USDT. Hidden balances must remain masked, and an unknown amount must remain a dash. Cancel without submitting.
Tap Hide details (id "UsdtDepositDetails"), then Details again. Verify the provider status remains separate from wallet activity and the entered refund address is retained.
Open completed, refunded and refund-in-progress deposit fixtures. Verify Request Refund is unavailable and the current provider status and available refund transaction are shown. An unsupported asset or network must show support guidance instead of a refund form.
While Cross-Network Deposits is open, inject a confirmed incoming USDT transfer through the isolated fixture. Verify no received sheet interrupts deposit details or refund review. Return to the USDT wallet and verify the received celebration appears once after any active sheet is dismissed. Dismiss it and revisit deposit details, then return to the wallet; verify the same receipt does not appear again.
diff --git a/journeys/usdt-number-format.xml b/journeys/usdt-number-format.xml
new file mode 100644
index 0000000000..71d6167191
--- /dev/null
+++ b/journeys/usdt-number-format.xml
@@ -0,0 +1,12 @@
+
+ Use an onboarded unfunded test wallet with USDT enabled. Set the system region to Germany or the Netherlands, keep the app language English, and restart the app. Use the same region on both platforms. No payment is submitted; a configured offline USDT provider is sufficient for unknown balances and amount entry. Confirmed empty-balance checks require a successful read from an owned local test provider.
+
+ Set the wallet display unit to dollars. On Home with the provider offline after relaunch, verify the USDT tile shows — rather than zero. After a successful balance read, verify a confirmed empty wallet shows $ 0.00 and loaded USDT balances use the same dot decimal separator as Savings and Spending.
+ Open USDT. Verify an unknown balance shows $ — and a confirmed empty balance shows $ 0.00. No refresh-error or history-sync copy appears underneath, and Send and Receive remain available.
+ Return Home and hide balances. Verify the USDT tile is masked. Open USDT and verify its balance is masked. Restore visible balances.
+ Open Receive, then Edit amount. Verify the decimal key is a dot. Enter 3.50 and verify the entered amount retains its trailing zero. Continue and verify the QR payment URI encodes 3500000 atomic units (uint256=3500000).
+ Open Show Details and verify the receiving address matches the QR recipient. Return to the QR and edit the amount to 0.000001. Verify all six decimals remain visible while editing and the QR payment URI encodes one atomic unit (uint256=1).
+ Return Home. Open Send, Enter Manually, and enter 0x1111111111111111111111111111111111111111. Continue to the USDT amount screen. Verify its decimal key is a dot and entering 1.25 displays $ 1.25. Close without submitting.
+ Repeat the unknown and confirmed empty-balance and amount-entry checks with the system region set to United States. Verify the separators and exact entered values match.
+
+
diff --git a/journeys/usdt-wallet-navigation.xml b/journeys/usdt-wallet-navigation.xml
index a742c3cb99..1332329977 100644
--- a/journeys/usdt-wallet-navigation.xml
+++ b/journeys/usdt-wallet-navigation.xml
@@ -1,9 +1,10 @@
- Use an onboarded wallet in an E2E build with USDT RPC and bundler URLs configured. No chain funding or live provider is required. A network error may be visible when services are offline; no payment is submitted.
+ Use an onboarded wallet in an E2E build with USDT RPC and bundler URLs configured. No chain funding or live provider is required. Wallet syncing stays silent when services are offline; no payment is submitted.
Enable USDT in the introductory sheet or Settings, General, Payments before starting this flow.
On Home, verify the Savings/Spending and USDT balance dividers align vertically, including any hardware wallet rows. With an odd number of paired hardware wallets, verify USDT fills the last row’s empty cell; with an even number, it starts the next row.
Verify USDT balances and activity amounts show at most two decimals; a nonzero amount below one cent shows <0.01. Open USDT from Home and verify the wallet screen uses the same balance, action buttons and activity layout as the Bitcoin wallet, with its Tether coin illustration anchored at the upper right.
+ Before the first successful balance read, verify Home shows — and the USDT wallet shows $ —. A confirmed empty balance must show $ 0.00. A known nonzero balance must remain visible if a later refresh fails; after relaunching offline it must show unknown rather than zero. Verify no red refresh error or grey history-sync copy appears below the balance while the provider is offline.
Open Receive. Verify the network is Arbitrum One. Open details, verify the address starts with 0x, and verify Details Copy contains only the address. Return to the QR and verify Copy and tapping the QR both copy only the same address, while the QR still contains the full USDT payment URI including the network and token. On Android, system Back from Details returns to the USDT QR; Back from the QR closes Receive without switching to Savings.
Close Receive, open Send and manual entry. Enter an invalid Bitcoin address and verify the address error prevents continuing.
Replace it with an owned valid EVM address. Continue to Amount, enter six decimal places and try a seventh; verify the amount remains limited to six. Close before requesting a quote.