From 23d1d5a06843ab0e0ac44e1da37b59da5cf3c098 Mon Sep 17 00:00:00 2001 From: SeniorZhai Date: Tue, 18 Aug 2026 13:50:13 +0800 Subject: [PATCH 1/3] feat(perps): handle leverage tier limits --- .../trade/perps/LiquidationPriceRequest.kt | 35 +++++++++++++++ .../home/web3/trade/perps/OpenPositionPage.kt | 24 +++++++++- .../web3/trade/perps/PerpetualViewModel.kt | 1 + .../PerpsAddBottomSheetDialogFragment.kt | 24 +++++++++- .../one/mixin/android/util/ErrorHandler.kt | 4 ++ app/src/main/res/values-ko/strings.xml | 2 + app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values-zh-rTW/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + .../perps/LiquidationPriceRequestTest.kt | 44 +++++++++++++++++++ 10 files changed, 136 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequest.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequest.kt index 27c133acde..b4fab5d11b 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequest.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequest.kt @@ -1,11 +1,20 @@ package one.mixin.android.ui.home.web3.trade.perps +import com.google.gson.JsonElement import kotlinx.coroutines.delay +import one.mixin.android.util.ErrorHandler import java.math.BigDecimal +internal data class LiquidationPriceLimit( + val maxAmount: String?, + val maxLeverage: Int?, +) + internal sealed interface LiquidationPriceResult { data class Success(val price: String) : LiquidationPriceResult + data class LimitExceeded(val limit: LiquidationPriceLimit) : LiquidationPriceResult + data object Retry : LiquidationPriceResult data object Failure : LiquidationPriceResult @@ -14,15 +23,36 @@ internal sealed interface LiquidationPriceResult { internal fun liquidationPriceResult( price: String?, errorCode: Int?, + limit: LiquidationPriceLimit = LiquidationPriceLimit(null, null), ): LiquidationPriceResult { val validPrice = price?.takeIf { it.isNotBlank() } return when { validPrice != null -> LiquidationPriceResult.Success(validPrice) + errorCode == ErrorHandler.PERPS_POSITION_SIZE_EXCEEDS_LEVERAGE_LIMIT -> { + LiquidationPriceResult.LimitExceeded(limit) + } errorCode == 500 -> LiquidationPriceResult.Retry else -> LiquidationPriceResult.Failure } } +internal fun parseLiquidationPriceLimit(extra: JsonElement?): LiquidationPriceLimit { + val extraObject = extra?.takeIf { it.isJsonObject }?.asJsonObject + val maxAmount = extraObject + ?.get("max_amount") + ?.takeIf { !it.isJsonNull } + ?.runCatching { asString } + ?.getOrNull() + ?.takeIf { it.isNotBlank() } + val maxLeverage = extraObject + ?.get("max_leverage") + ?.takeIf { !it.isJsonNull } + ?.runCatching { asString.toIntOrNull() } + ?.getOrNull() + ?.takeIf { it > 0 } + return LiquidationPriceLimit(maxAmount, maxLeverage) +} + internal fun shouldRequestLiquidationPrice( amount: BigDecimal?, minimumAmount: BigDecimal, @@ -34,11 +64,16 @@ internal fun shouldRequestLiquidationPrice( internal suspend fun requestLiquidationPrice( retryDelayMillis: Long = 1000L, + onLimitExceeded: (LiquidationPriceLimit) -> Unit = {}, request: suspend () -> LiquidationPriceResult, ): String? { while (true) { when (val result = request()) { LiquidationPriceResult.Failure -> return null + is LiquidationPriceResult.LimitExceeded -> { + onLimitExceeded(result.limit) + return null + } LiquidationPriceResult.Retry -> delay(retryDelayMillis) is LiquidationPriceResult.Success -> return result.price } diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/OpenPositionPage.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/OpenPositionPage.kt index 2fcd9a9475..ed183c9855 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/OpenPositionPage.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/OpenPositionPage.kt @@ -149,6 +149,7 @@ fun OpenPositionPage( var takeProfitPrice by remember { mutableStateOf("") } var stopLossPrice by remember { mutableStateOf("") } var remoteLiquidationPrice by remember { mutableStateOf(null) } + var liquidationPriceLimit by remember { mutableStateOf(null) } var isLiquidationLoading by remember { mutableStateOf(false) } var errorInfo by remember { mutableStateOf(null) } var isProcessing by remember { mutableStateOf(false) } @@ -221,6 +222,7 @@ fun OpenPositionPage( } LaunchedEffect(usdtAmount, leverage, currentMarket.minAmount) { + liquidationPriceLimit = null val amount = usdtAmount.toBigDecimalOrNull() val minimumAmount = currentMarket.minAmount.toBigDecimalOrNull() ?: BigDecimal.ZERO if (!shouldRequestLiquidationPrice(amount, minimumAmount)) { @@ -238,7 +240,9 @@ fun OpenPositionPage( .stripTrailingZeros() .toPlainString() .let { limitTradeInputDecimalPlaces(it, TRADE_INPUT_MAX_DECIMAL_PLACES) } - remoteLiquidationPrice = requestLiquidationPrice { + remoteLiquidationPrice = requestLiquidationPrice( + onLimitExceeded = { liquidationPriceLimit = it }, + ) { viewModel.estimateLiquidationPrice( marketId = currentMarket.marketId, amount = normalizedAmount, @@ -276,13 +280,29 @@ fun OpenPositionPage( maximumMargin.stripTrailingZeros().toPlainString(), currentToken?.symbol.orEmpty(), ) + val liquidationLimitMaxAmount = liquidationPriceLimit?.maxAmount + ?.toBigDecimalOrNull() + ?.takeIf { it > BigDecimal.ZERO } + ?.stripTrailingZeros() + ?.toPlainString() + val liquidationLimitAmountError = liquidationLimitMaxAmount?.let { + stringResource(R.string.perps_maximum_margin, it, currentToken?.symbol.orEmpty()) + } + val liquidationLimitLeverageError = liquidationPriceLimit?.maxLeverage?.let { + stringResource(R.string.perps_maximum_leverage, it) + } + val liquidationLimitError = liquidationPriceLimit?.let { + listOfNotNull(liquidationLimitAmountError, liquidationLimitLeverageError) + .joinToString(". ") + .ifBlank { stringResource(R.string.error_perps_position_size_exceeds_leverage_limit) } + } val displayLiquidationPrice = remoteLiquidationPrice val marginLimitError = when { belowMinimumMargin -> minimumMarginError aboveMaximumMargin -> maximumMarginError else -> null } - val displayedErrorInfo = errorInfo?.takeIf { it.isNotBlank() } ?: marginLimitError + val displayedErrorInfo = errorInfo?.takeIf { it.isNotBlank() } ?: marginLimitError ?: liquidationLimitError val tokenNetworkName = currentToken?.chainName ?.takeIf { it.isNotBlank() } ?: currentToken?.chainSymbol diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpetualViewModel.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpetualViewModel.kt index 013577d47b..6b4b54926f 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpetualViewModel.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpetualViewModel.kt @@ -403,6 +403,7 @@ class PerpetualViewModel @Inject constructor( liquidationPriceResult( price = null, errorCode = response.errorCode, + limit = parseLiquidationPriceLimit(response.error?.extra), ) } } catch (e: CancellationException) { diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpsAddBottomSheetDialogFragment.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpsAddBottomSheetDialogFragment.kt index 90b231218b..705f42ea84 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpsAddBottomSheetDialogFragment.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/perps/PerpsAddBottomSheetDialogFragment.kt @@ -278,6 +278,7 @@ private fun PerpsAddContent( val viewModel = hiltViewModel() var amount by remember(position.positionId) { mutableStateOf("") } var remoteLiquidationPrice by remember(position.positionId) { mutableStateOf(null) } + var liquidationPriceLimit by remember(position.positionId) { mutableStateOf(null) } var isLiquidationLoading by remember(position.positionId) { mutableStateOf(false) } var liquidationJob by remember(position.positionId) { mutableStateOf(null) } val tokenBalance = selectedToken?.balance?.toBigDecimalOrNull() ?: BigDecimal.ZERO @@ -305,6 +306,7 @@ private fun PerpsAddContent( val priceScale = market?.priceScale ?: position.priceScale LaunchedEffect(amount, belowMinimumMargin, aboveMaximumMargin) { + liquidationPriceLimit = null val addMargin = amount.toBigDecimalOrNull() if (!shouldRequestLiquidationPrice(addMargin, minimumMargin) || aboveMaximumMargin) { liquidationJob?.cancel() @@ -322,7 +324,9 @@ private fun PerpsAddContent( .stripTrailingZeros() .toPlainString() .let { limitTradeInputDecimalPlaces(it, TRADE_INPUT_MAX_DECIMAL_PLACES) } - remoteLiquidationPrice = requestLiquidationPrice { + remoteLiquidationPrice = requestLiquidationPrice( + onLimitExceeded = { liquidationPriceLimit = it }, + ) { viewModel.estimateLiquidationPrice( amount = normalizedAmount, positionId = position.positionId, @@ -342,10 +346,26 @@ private fun PerpsAddContent( maximumMargin.stripTrailingZeros().toPlainString(), selectedToken?.symbol.orEmpty(), ) + val liquidationLimitMaxAmount = liquidationPriceLimit?.maxAmount + ?.toBigDecimalOrNull() + ?.takeIf { it > BigDecimal.ZERO } + ?.stripTrailingZeros() + ?.toPlainString() + val liquidationLimitAmountError = liquidationLimitMaxAmount?.let { + stringResource(R.string.perps_maximum_margin, it, selectedToken?.symbol.orEmpty()) + } + val liquidationLimitLeverageError = liquidationPriceLimit?.maxLeverage?.let { + stringResource(R.string.perps_maximum_leverage, it) + } + val liquidationLimitError = liquidationPriceLimit?.let { + listOfNotNull(liquidationLimitAmountError, liquidationLimitLeverageError) + .joinToString(". ") + .ifBlank { stringResource(R.string.error_perps_position_size_exceeds_leverage_limit) } + } val marginLimitError = when { belowMinimumMargin -> minimumMarginError aboveMaximumMargin -> maximumMarginError - else -> null + else -> liquidationLimitError } val currentPriceText = formatPerpsPrice(currentPrice, priceScale) diff --git a/app/src/main/java/one/mixin/android/util/ErrorHandler.kt b/app/src/main/java/one/mixin/android/util/ErrorHandler.kt index cc02357c06..6f9a69b12d 100644 --- a/app/src/main/java/one/mixin/android/util/ErrorHandler.kt +++ b/app/src/main/java/one/mixin/android/util/ErrorHandler.kt @@ -226,6 +226,7 @@ open class ErrorHandler { const val MAX_WALLET_REACHED = 10632 const val PERPS_ORDER_VALUE_TOO_SMALL = 10650 const val PERPS_MARKET_ALREADY_HAS_ACTIVE_POSITION = 10651 + const val PERPS_POSITION_SIZE_EXCEEDS_LEVERAGE_LIMIT = 10655 const val UNSUPPORTED_WATCH_ADDRESS = 10633 const val INVALID_REFERRAL_CODE = 10730 @@ -336,6 +337,9 @@ fun Context.getMixinErrorStringByCode( ErrorHandler.PERPS_MARKET_ALREADY_HAS_ACTIVE_POSITION -> { getString(R.string.error_already_had_open_position) } + ErrorHandler.PERPS_POSITION_SIZE_EXCEEDS_LEVERAGE_LIMIT -> { + getString(R.string.error_perps_position_size_exceeds_leverage_limit) + } ErrorHandler.UNSUPPORTED_WATCH_ADDRESS -> { getString(R.string.error_watch_address_not_supported) } diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 910ed756df..e1d7c46d8f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -487,6 +487,7 @@ 오류 10650: 위치 크기가 너무 작습니다. 오류 10651: 이미 오픈 포지션이 있습니다. 오류 10652: 현재 시장에서 아직 다른 주문을 처리 중입니다. 나중에 다시 시도하십시오. + 오류 10655: 포지션 크기가 선택한 레버리지 한도를 초과했습니다. 금액 또는 레버리지를 낮추세요. 오류 20114: 만료된 전화 확인 코드 오류 20113: 잘못된 전화 확인 코드 5회 이상 시도하셨습니다. 다시 시도하려면 최소 24시간 정도 기다려 주십시오. @@ -1649,6 +1650,7 @@ 금액은 %1$s %2$s에서 %3$s %4$s 사이여야 합니다. 최소 금액은 %1$s %2$s입니다. 최대 금액은 %1$s %2$s입니다. + 이 금액의 최대 레버리지는 %1$dx입니다. 승인 대출 굽기 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9c38f1b42f..72ee405505 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -483,6 +483,7 @@ 错误 10650: 仓位规模太小,请调整后重试。 错误 10651:当前已有持仓 当前市场已开仓。 + 错误 10655:仓位规模超过当前杠杆档位限制,请降低金额或杠杆。 错误 20114:验证码已过期 错误 20113:验证码错误 你已经尝试了超过 5 次,请等待 24 小时后再次尝试。 @@ -1623,6 +1624,7 @@ 金额范围 %1$s %2$s ~ %3$s %4$s 最小金额为 %1$s %2$s 最大金额为 %1$s %2$s + 当前金额支持的最大杠杆为 %1$dx 授权 借款 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0878f9ad97..f88237edf9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1305,6 +1305,7 @@ 錯誤 10650:部位規模太小。 錯誤 10651:當前已有持倉 錯誤10652:目前市場仍在處理其他訂單,請稍後重試。 + 錯誤 10655:部位規模超過目前槓桿檔位限制,請降低金額或槓桿。 錯誤 10633:地址不可用 錯誤 10730:無效的邀請碼 錯誤 10731:當前賬號已繫結邀請碼,無法修改繫結。 @@ -1768,6 +1769,7 @@ 金額必須介於 %1$s %2$s 與 %3$s %4$s 之間 最低金額為 %1$s %2$s 最大金額為%1$s %2$s + 目前金額支援的最大槓桿為 %1$dx 批准 借款 燃燒 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fff76e95aa..dc61d5637d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -489,6 +489,7 @@ ERROR 10650: Position size is too small. ERROR 10651: You already had a open position ERROR 10652: The current market is still processing other orders, please try again later. + ERROR 10655: Position size exceeds the limit for the selected leverage. Reduce the amount or leverage. ERROR 20114: Expired phone verification code ERROR 20113: Invalid phone verification code You have tried more than 5 times, please wait at least 24 hours to try again. @@ -1668,6 +1669,7 @@ Amount must be between %1$s %2$s and %3$s %4$s The minimum amount is %1$s %2$s The maximum amount is %1$s %2$s + The maximum leverage for this amount is %1$dx Approve Borrow diff --git a/app/src/test/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequestTest.kt b/app/src/test/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequestTest.kt index 21c69f6ec2..4c65ab5887 100644 --- a/app/src/test/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequestTest.kt +++ b/app/src/test/java/one/mixin/android/ui/home/web3/trade/perps/LiquidationPriceRequestTest.kt @@ -1,6 +1,8 @@ package one.mixin.android.ui.home.web3.trade.perps +import com.google.gson.JsonParser import kotlinx.coroutines.runBlocking +import one.mixin.android.util.ErrorHandler import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertSame @@ -52,6 +54,48 @@ class LiquidationPriceRequestTest { LiquidationPriceResult.Failure, liquidationPriceResult(price = null, errorCode = null), ) + assertEquals( + LiquidationPriceResult.LimitExceeded( + LiquidationPriceLimit(maxAmount = "100", maxLeverage = 5), + ), + liquidationPriceResult( + price = null, + errorCode = ErrorHandler.PERPS_POSITION_SIZE_EXCEEDS_LEVERAGE_LIMIT, + limit = LiquidationPriceLimit(maxAmount = "100", maxLeverage = 5), + ), + ) + } + + @Test + fun leverageLimitStopsAndReturnsRetryValues() = runBlocking { + var requestCount = 0 + var limit: LiquidationPriceLimit? = null + + val price = requestLiquidationPrice( + retryDelayMillis = 0L, + onLimitExceeded = { limit = it }, + ) { + requestCount += 1 + LiquidationPriceResult.LimitExceeded( + LiquidationPriceLimit(maxAmount = "25.5", maxLeverage = 3), + ) + } + + assertNull(price) + assertEquals(1, requestCount) + assertEquals(LiquidationPriceLimit(maxAmount = "25.5", maxLeverage = 3), limit) + } + + @Test + fun parsesLeverageLimitExtra() { + val extra = JsonParser.parseString( + """{"max_amount":"42.50","max_leverage":7}""", + ) + + assertEquals( + LiquidationPriceLimit(maxAmount = "42.50", maxLeverage = 7), + parseLiquidationPriceLimit(extra), + ) } @Test From b63cf69efef5ca0f2facf5c89040c9dd781ccf5d Mon Sep 17 00:00:00 2001 From: SeniorZhai Date: Wed, 19 Aug 2026 10:59:51 +0800 Subject: [PATCH 2/3] fix(strings): update unsupported watch address message --- app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-ms/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 8 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7881d17587..ad3d4ff806 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -419,6 +419,7 @@ ERROR: %1$d ERROR: %1$s ERROR 20122: Este número de teléfono ya está asociado a otra cuenta. + ERROR 10633: Actualmente no se admite añadir esta dirección ID de usuario invalido ERROR 20131: Formato de nota de retiro incorrecto. Todos diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 386b7e1696..3f6758fe68 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -265,6 +265,7 @@ KESALAHAN %1$d KESALAHAN: %1$s KESALAHAN 20122: Ponsel digunakan oleh orang lain. + ERROR 10633: Saat ini belum mendukung penambahan alamat ini ID pengguna tidak valid KESALAHAN 20131: Format memo penarikan salah. Semua orang diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 9730327234..ffdbb63c5e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -394,6 +394,7 @@ エラー:%1$d エラー:%1$s エラー20122:この電話番号はすでに他のアカウントと紐づけられています + エラー 10633: このアドレスは現在対応していません 無効なユーザーIDです エラー20131:出金メモのフォーマットが不正確です 全員 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e1d7c46d8f..5f47094a60 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -510,7 +510,7 @@ 오류 %1$d 오류: %1$s 오류 20122: 이 전화번호는 이미 다른 계정과 연결되어 있습니다. - 오류 10633: 잘못된 주소 + 오류 10633: 현재 이 주소를 추가할 수 없습니다 오류 10730: 잘못된 추천 코드 오류 10731: 이 계정은 이미 추천 코드를 적용했습니다. 오류 10732: 자신의 추천 코드를 적용할 수 없습니다. diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 6d9f2d731d..ca1f50542e 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -284,6 +284,7 @@ RALAT %1$d RALAT: %1$s RALAT 20122: Telefon digunakan oleh orang lain. + RALAT 10633: Penambahan alamat ini tidak disokong buat masa ini Id pengguna tidak sah RALAT 20131: Penarikan format memo tidak betul. Semua orang diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index aa7b80dabd..6d89ce9dea 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -404,6 +404,7 @@ ОШИБКА %1$d ОШИБКА: %1$s ОШИБКА 20122: этот номер телефона уже связан с другой учетной записью. + ОШИБКА 10633: Добавление этого адреса в настоящее время не поддерживается Недопустимый идентификатор пользователя ОШИБКА 20131: Неверный формат уведомления о снятии средств. Все diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 72ee405505..09ed3b227d 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -506,7 +506,7 @@ 错误:%1$d 错误:%1$s 错误 20122:电话号码已经被占用。 - 错误 10633:地址不可用 + 错误 10633:暂不支持添加该地址 错误 10730:无效的邀请码 成交 错误 10731:当前账户已绑定邀请码,无法修改绑定。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc61d5637d..f4933a8372 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -512,7 +512,7 @@ ERROR %1$d ERROR: %1$s ERROR 20122: This phone number is already associated with another account. - ERROR 10633: Invalid Address + ERROR 10633: This address is currently not supported ERROR 10730: Invalid Referral Code ERROR 10731: This account has already applied a referral code ERROR 10732: Cannot apply your own referral code From 8647387ce0e94b017a73042f9c72a6c27339fe4f Mon Sep 17 00:00:00 2001 From: SeniorZhai Date: Thu, 27 Aug 2026 14:56:29 +0800 Subject: [PATCH 3/3] fix(perps): localize minimum order value error --- .../one/mixin/android/util/ErrorHandler.kt | 18 +++++++++++++++--- app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-ms/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- .../one/mixin/android/util/ErrorHandlerTest.kt | 15 +++++++++++++++ 11 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 app/src/test/java/one/mixin/android/util/ErrorHandlerTest.kt diff --git a/app/src/main/java/one/mixin/android/util/ErrorHandler.kt b/app/src/main/java/one/mixin/android/util/ErrorHandler.kt index 6f9a69b12d..3c7ecacb56 100644 --- a/app/src/main/java/one/mixin/android/util/ErrorHandler.kt +++ b/app/src/main/java/one/mixin/android/util/ErrorHandler.kt @@ -1,6 +1,7 @@ package one.mixin.android.util import android.content.Context +import androidx.annotation.StringRes import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineExceptionHandler import one.mixin.android.MixinApplication @@ -226,6 +227,7 @@ open class ErrorHandler { const val MAX_WALLET_REACHED = 10632 const val PERPS_ORDER_VALUE_TOO_SMALL = 10650 const val PERPS_MARKET_ALREADY_HAS_ACTIVE_POSITION = 10651 + const val PERPS_ORDER_VALUE_BELOW_MINIMUM = 10654 const val PERPS_POSITION_SIZE_EXCEEDS_LEVERAGE_LIMIT = 10655 const val UNSUPPORTED_WATCH_ADDRESS = 10633 @@ -271,6 +273,10 @@ fun Context.getMixinErrorStringByCode( code: Int, message: String, ): String { + perpsOrderValueErrorResource(code)?.let { resource -> + return getString(resource, code) + } + return when (code) { ErrorHandler.TRANSACTION -> "${ErrorHandler.TRANSACTION} TRANSACTION" ErrorHandler.BAD_DATA -> { @@ -331,9 +337,6 @@ fun Context.getMixinErrorStringByCode( ErrorHandler.MAX_WALLET_REACHED -> { getString(R.string.error_too_many_wallets) } - ErrorHandler.PERPS_ORDER_VALUE_TOO_SMALL -> { - getString(R.string.error_perps_order_value_too_small) - } ErrorHandler.PERPS_MARKET_ALREADY_HAS_ACTIVE_POSITION -> { getString(R.string.error_already_had_open_position) } @@ -446,3 +449,12 @@ fun Context.getMixinErrorStringByCode( } } } + +@StringRes +internal fun perpsOrderValueErrorResource(code: Int): Int? = + when (code) { + ErrorHandler.PERPS_ORDER_VALUE_TOO_SMALL, + ErrorHandler.PERPS_ORDER_VALUE_BELOW_MINIMUM, + -> R.string.error_perps_order_value_too_small + else -> null + } diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ad3d4ff806..2267f08bca 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -394,6 +394,7 @@ ERROR 10006: Actualiza Mixin(%1$s) para seguir usando el servicio. No puedo encontrar una aplicación de mapas Por favor abre los permisos necesarios + ERROR %1$d: El tamaño de la posición es demasiado pequeño. ERROR 20110: Número de teléfono no válido ERROR 10003: Error al entregar SMS ERROR 20114: Código de verificación del teléfono caducado diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 3f6758fe68..027b99c893 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -246,6 +246,7 @@ KESALAHAN 10006: Harap perbarui Mixin(%1$s) untuk terus menggunakan layanan. Tidak dapat menemukan aplikasi peta Harap buka izin yang diperlukan + KESALAHAN %1$d: Ukuran posisi terlalu kecil. KESALAHAN 20110: Nomor telepon tidak valid KESALAHAN 10003: Gagal mengirim SMS KESALAHAN 20114: Kode verifikasi telepon sudah tidak berlaku diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ffdbb63c5e..93092bc015 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -370,6 +370,7 @@ エラー 10006:このサービスを引き続き使用するには、Mixin(%1$s)をアップデートしてください。 地図アプリがありません 必要な権限を開いてください + エラー %1$d:ポジションサイズが小さすぎます。 エラー 20110:無効な電話番号です エラー 10003:SMSの送信に失敗しました 期限切れ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 5f47094a60..61fcd6afd2 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -484,7 +484,7 @@ 오류 10614: 입력 금액이 너무 작거나 너무 큽니다. 금액을 조정하세요. 오류 10614: 금액이 %1$s의 최대 허용 값을 초과했습니다. 금액을 조정해주세요. 지정가 주문을 해보시겠어요? 금액이나 토큰에 제한이 없습니다. 오류 10615: 이 거래 쌍은 현재 지원되지 않습니다. 다른 토큰으로 전환해 보십시오. - 오류 10650: 위치 크기가 너무 작습니다. + 오류 %1$d: 포지션 크기가 너무 작습니다. 오류 10651: 이미 오픈 포지션이 있습니다. 오류 10652: 현재 시장에서 아직 다른 주문을 처리 중입니다. 나중에 다시 시도하십시오. 오류 10655: 포지션 크기가 선택한 레버리지 한도를 초과했습니다. 금액 또는 레버리지를 낮추세요. diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index ca1f50542e..9d4022b092 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -265,6 +265,7 @@ RALAT 10006: Sila kemas kini Mixin(%1$s) untuk terus menggunakan perkhidmatan ini. Tidak dapat mencari aplikasi peta Sila buka kebenaran yang diperlukan + RALAT %1$d: Saiz posisi terlalu kecil. RALAT 20110: Nombor telefon tidak sah RALAT 10003: Gagal menghantar SMS RALAT 20114: Kod pengesahan telefon yang telah tamat tempoh diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6d89ce9dea..ea1e2ba6d8 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -377,6 +377,7 @@ ОШИБКА 10006: Обновите Mixin(%1$s), чтобы продолжить использование сервиса. Не могу найти приложение карты Пожалуйста, Откройте необходимые разрешения + ОШИБКА %1$d: Размер позиции слишком мал. ОШИБКА 20110: Неверный номер телефона ОШИБКА 10003: Не удалось доставить SMS ОШИБКА 20114: Срок действия кода подтверждения телефона истек diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 09ed3b227d..63400f9d1b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -480,7 +480,7 @@ 错误 10614: 输入金额太小或太大,请重新输入。 错误 10614: 金额超出最大下单金额 %1$s,请重新输入。试试限价?不受金额和币种限制。 错误 10615: 暂不支持该交易对,请尝试切换币种。 - 错误 10650: 仓位规模太小,请调整后重试。 + 错误 %1$d: 仓位规模太小,请调整后重试。 错误 10651:当前已有持仓 当前市场已开仓。 错误 10655:仓位规模超过当前杠杆档位限制,请降低金额或杠杆。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f88237edf9..60bce948a0 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1302,7 +1302,7 @@ 錯誤 10614: 輸入金額太小或太大,請重新輸入。 錯誤 10614: 金額超出最大下單金額 %1$s,請重新輸入。試試專業模式?不受金額和幣種限制。 錯誤10615:目前不支援此交易對,請嘗試切換到其他代幣。 - 錯誤 10650:部位規模太小。 + 錯誤 %1$d:部位規模太小。 錯誤 10651:當前已有持倉 錯誤10652:目前市場仍在處理其他訂單,請稍後重試。 錯誤 10655:部位規模超過目前槓桿檔位限制,請降低金額或槓桿。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4933a8372..4b8dc1ca5f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -486,7 +486,7 @@ ERROR 10614: The input amount is either too small or too large, please adjust the amount. ERROR 10614: The amount exceeds the maximum allowable value of %1$s. Please adjust the amount. Try placing a limit order? No restrictions on amount or token. ERROR 10615: This trading pair is currently not supported, please try switching to a different token. - ERROR 10650: Position size is too small. + ERROR %1$d: Position size is too small. ERROR 10651: You already had a open position ERROR 10652: The current market is still processing other orders, please try again later. ERROR 10655: Position size exceeds the limit for the selected leverage. Reduce the amount or leverage. diff --git a/app/src/test/java/one/mixin/android/util/ErrorHandlerTest.kt b/app/src/test/java/one/mixin/android/util/ErrorHandlerTest.kt new file mode 100644 index 0000000000..3b9c8233b9 --- /dev/null +++ b/app/src/test/java/one/mixin/android/util/ErrorHandlerTest.kt @@ -0,0 +1,15 @@ +package one.mixin.android.util + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import one.mixin.android.R + +class ErrorHandlerTest { + @Test + fun mapsPerpsOrderValueBelowMinimumErrors() { + assertEquals(R.string.error_perps_order_value_too_small, perpsOrderValueErrorResource(10650)) + assertEquals(R.string.error_perps_order_value_too_small, perpsOrderValueErrorResource(10654)) + assertNull(perpsOrderValueErrorResource(10653)) + } +}