Updated on 2026-08-14
This commit is contained in:
commit
bec9e17f51
93 changed files with 1335 additions and 605 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 544358e4640ae25402dd4186e0f244f9509c8906
|
||||
Subproject commit adbdabe422b0513640d1750f276298683d342f61
|
||||
|
|
@ -268,6 +268,16 @@ internal object TokensDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetMinimumTransactionAmountSyncUseCase(
|
||||
currencyChecksRepository: CurrencyChecksRepository,
|
||||
): GetMinimumTransactionAmountSyncUseCase {
|
||||
return GetMinimumTransactionAmountSyncUseCase(
|
||||
currencyChecksRepository = currencyChecksRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsCryptoCurrencyCoinCouldHideUseCase(
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
Timber.i("onSessionDelete: $sessionDelete")
|
||||
}
|
||||
|
||||
override fun onSessionExtend(session: Wallet.Model.Session) {
|
||||
Timber.i("onSessionExtend: $session")
|
||||
}
|
||||
|
||||
override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) {
|
||||
// Triggered when wallet receives the session settlement response from Dapp
|
||||
Timber.i("onSessionSettleResponse: $settleSessionResponse")
|
||||
|
|
@ -264,8 +268,6 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
}
|
||||
|
||||
override fun approve(userNamespaces: Map<NetworkNamespace, List<Account>>) {
|
||||
this.userNamespaces = userNamespaces
|
||||
|
||||
val sessionProposal: Wallet.Model.SessionProposal = requireNotNull(this.sessionProposal)
|
||||
|
||||
val userChains = userNamespaces.flatMap { namespace ->
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase
|
||||
import com.tangem.domain.balancehiding.BalanceHidingSettings
|
||||
|
|
@ -99,7 +98,6 @@ internal class MainViewModel @Inject constructor(
|
|||
.distinctUntilChanged()
|
||||
.onEach { userWallet ->
|
||||
Analytics.setContext(userWallet.scanResponse)
|
||||
Analytics.send(Basic.WalletOpened())
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,15 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.amountScreen.utils.checkExceedBalance
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatValue
|
||||
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
|
|
@ -22,6 +27,7 @@ import java.math.BigDecimal
|
|||
*/
|
||||
class AmountReduceByTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: ReduceByData,
|
||||
) : Transformer<AmountState> {
|
||||
|
||||
|
|
@ -47,22 +53,37 @@ class AmountReduceByTransformer(
|
|||
|
||||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField)
|
||||
val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false
|
||||
val isZero = if (amountTextField.isFiatValue) {
|
||||
decimalFiatValue.isNullOrZero()
|
||||
} else {
|
||||
decimalCryptoValue.isNullOrZero()
|
||||
}
|
||||
val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided
|
||||
return prevState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance && !isZero,
|
||||
isPrimaryButtonEnabled = !isZero && !isCheckFailed,
|
||||
amountTextField = amountTextField.copy(
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = isExceedBalance,
|
||||
error = resourceReference(R.string.send_validation_amount_exceeds_balance),
|
||||
isError = isCheckFailed,
|
||||
error = when {
|
||||
isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance)
|
||||
isLessThanMinimumIfProvided -> {
|
||||
val minimumAmount = minimumTransactionAmount
|
||||
?.amount
|
||||
?.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
.orEmpty()
|
||||
resourceReference(
|
||||
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
|
||||
wrappedList(minimumAmount, minimumAmount),
|
||||
)
|
||||
}
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue),
|
||||
imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue),
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,17 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.amountScreen.utils.checkExceedBalance
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatValue
|
||||
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
|
@ -22,6 +28,7 @@ import java.math.BigDecimal
|
|||
*/
|
||||
class AmountReduceToTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: BigDecimal,
|
||||
) : Transformer<AmountState> {
|
||||
private val maxEnterAmountConverter = MaxEnterAmountConverter()
|
||||
|
|
@ -34,6 +41,7 @@ class AmountReduceToTransformer(
|
|||
val fiatDecimals = amountTextField.fiatAmount.decimals
|
||||
|
||||
val cryptoValue = value.parseBigDecimal(cryptoDecimals)
|
||||
val decimalCryptoValue = cryptoValue.parseToBigDecimal(cryptoDecimals)
|
||||
val (fiatValue, decimalFiatValue) = cryptoValue.getFiatValue(
|
||||
fiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
isFiatValue = false,
|
||||
|
|
@ -44,18 +52,33 @@ class AmountReduceToTransformer(
|
|||
|
||||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField)
|
||||
val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false
|
||||
val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else value.isNullOrZero()
|
||||
val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided
|
||||
return prevState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance && !isZero,
|
||||
isPrimaryButtonEnabled = !isZero && !isCheckFailed,
|
||||
amountTextField = amountTextField.copy(
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = isExceedBalance,
|
||||
error = resourceReference(R.string.send_validation_amount_exceeds_balance),
|
||||
isError = isCheckFailed,
|
||||
error = when {
|
||||
isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance)
|
||||
isLessThanMinimumIfProvided -> {
|
||||
val minimumAmount = minimumTransactionAmount
|
||||
?.amount
|
||||
?.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
.orEmpty()
|
||||
resourceReference(
|
||||
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
|
||||
wrappedList(minimumAmount, minimumAmount),
|
||||
)
|
||||
}
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = value),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = getKeyboardAction(isExceedBalance, value),
|
||||
imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue),
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldConverter
|
|||
import com.tangem.common.ui.amountScreen.models.AmountParameters
|
||||
import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -26,7 +26,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*
|
||||
* @property clickIntents amount screen clicks
|
||||
* @property appCurrencyProvider selected app currency provider
|
||||
* @property maxEnterAmountProvider max enter amount data provider
|
||||
* @property maxEnterAmount max enter amount data
|
||||
* @property cryptoCurrencyStatusProvider current cryptocurrency status provider
|
||||
* @property iconStateConverter currency icon converter
|
||||
*/
|
||||
|
|
@ -34,7 +34,7 @@ class AmountStateConverter(
|
|||
private val clickIntents: AmountScreenClickIntents,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val maxEnterAmountProvider: Provider<MaxEnterAmount>,
|
||||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val iconStateConverter: CryptoCurrencyToIconStateConverter,
|
||||
) : Converter<AmountParameters, AmountState> {
|
||||
|
||||
|
|
@ -47,10 +47,9 @@ class AmountStateConverter(
|
|||
}
|
||||
|
||||
override fun convert(value: AmountParameters): AmountState {
|
||||
val maxEnterAmount = maxEnterAmountProvider()
|
||||
val appCurrency = appCurrencyProvider()
|
||||
val status = cryptoCurrencyStatusProvider()
|
||||
val fiat = formatFiatAmount(maxEnterAmount.fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
val fiat = maxEnterAmount.fiatAmount.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
val crypto = maxEnterAmount.amount.format { crypto(status.currency) }
|
||||
val noFeeRate = status.value.fiatRate.isNullOrZero()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package com.tangem.common.ui.amountScreen.converters
|
||||
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converts [CryptoCurrencyStatus] to [MaxEnterAmount]
|
||||
* Converts [CryptoCurrencyStatus] to [EnterAmountBoundary]
|
||||
*/
|
||||
class MaxEnterAmountConverter : Converter<CryptoCurrencyStatus, MaxEnterAmount> {
|
||||
class MaxEnterAmountConverter : Converter<CryptoCurrencyStatus, EnterAmountBoundary> {
|
||||
|
||||
override fun convert(value: CryptoCurrencyStatus): MaxEnterAmount {
|
||||
return MaxEnterAmount(
|
||||
override fun convert(value: CryptoCurrencyStatus): EnterAmountBoundary {
|
||||
return EnterAmountBoundary(
|
||||
amount = value.value.amount,
|
||||
fiatAmount = value.value.fiatAmount,
|
||||
fiatRate = value.value.fiatRate,
|
||||
|
|
|
|||
|
|
@ -5,14 +5,18 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.amountScreen.utils.checkExceedBalance
|
||||
import com.tangem.common.ui.amountScreen.utils.getCryptoValue
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatValue
|
||||
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -24,7 +28,9 @@ import java.math.BigDecimal
|
|||
* @property value amount value
|
||||
*/
|
||||
class AmountFieldChangeTransformer(
|
||||
private val maxEnterAmount: MaxEnterAmount,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: String,
|
||||
) : Transformer<AmountState> {
|
||||
|
||||
|
|
@ -52,23 +58,37 @@ class AmountFieldChangeTransformer(
|
|||
|
||||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField)
|
||||
val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false
|
||||
val isZero = if (amountTextField.isFiatValue) {
|
||||
decimalFiatValue.isNullOrZero()
|
||||
} else {
|
||||
decimalCryptoValue.isNullOrZero()
|
||||
}
|
||||
val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided
|
||||
return prevState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance && !isZero,
|
||||
isPrimaryButtonEnabled = !isZero && !isCheckFailed,
|
||||
amountTextField = amountTextField.copy(
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = isExceedBalance,
|
||||
error = resourceReference(R.string.send_validation_amount_exceeds_balance).takeIf { isExceedBalance }
|
||||
?: TextReference.EMPTY,
|
||||
isError = isCheckFailed,
|
||||
error = when {
|
||||
isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance)
|
||||
isLessThanMinimumIfProvided -> {
|
||||
val minimumAmount = minimumTransactionAmount
|
||||
?.amount
|
||||
?.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
.orEmpty()
|
||||
resourceReference(
|
||||
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
|
||||
wrappedList(minimumAmount, minimumAmount),
|
||||
)
|
||||
}
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue),
|
||||
imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue),
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
package com.tangem.common.ui.amountScreen.converters.field
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.RoundingMode
|
||||
|
||||
|
|
@ -16,7 +23,9 @@ import java.math.RoundingMode
|
|||
* @property maxAmount maximum enter amount
|
||||
*/
|
||||
class AmountFieldSetMaxAmountTransformer(
|
||||
private val maxAmount: MaxEnterAmount,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val maxAmount: EnterAmountBoundary,
|
||||
private val minAmount: EnterAmountBoundary?,
|
||||
) : Transformer<AmountState> {
|
||||
|
||||
override fun transform(prevState: AmountState): AmountState {
|
||||
|
|
@ -29,22 +38,35 @@ class AmountFieldSetMaxAmountTransformer(
|
|||
val decimalCryptoValue = maxAmount.amount
|
||||
val decimalFiatValue = maxAmount.fiatAmount
|
||||
|
||||
if (decimalCryptoValue.isNullOrZero()) return prevState
|
||||
if (decimalCryptoValue == null || decimalCryptoValue.isZero()) return prevState
|
||||
|
||||
val isDoneActionEnabled = !decimalCryptoValue.isNullOrZero()
|
||||
val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty()
|
||||
val cryptoValue = decimalCryptoValue.parseBigDecimal(cryptoDecimals)
|
||||
val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals, roundingMode = RoundingMode.HALF_UP).orEmpty()
|
||||
val isLessThanMinimumIfProvided = minAmount?.amount?.let { decimalCryptoValue < it } ?: false
|
||||
return prevState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
isPrimaryButtonEnabled = !isLessThanMinimumIfProvided,
|
||||
amountTextField = amountTextField.copy(
|
||||
isValuePasted = true,
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = false,
|
||||
isError = isLessThanMinimumIfProvided,
|
||||
error = when {
|
||||
isLessThanMinimumIfProvided -> {
|
||||
val minimumAmount = minAmount
|
||||
?.amount
|
||||
?.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
.orEmpty()
|
||||
resourceReference(
|
||||
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
|
||||
wrappedList(minimumAmount, minimumAmount),
|
||||
)
|
||||
}
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = if (isDoneActionEnabled) ImeAction.Done else ImeAction.None,
|
||||
imeAction = getKeyboardAction(isLessThanMinimumIfProvided, decimalCryptoValue),
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.common.ui.amountScreen.models
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class EnterAmountBoundary(
|
||||
val amount: BigDecimal? = null,
|
||||
val fiatAmount: BigDecimal? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
) {
|
||||
constructor(
|
||||
amount: BigDecimal? = null,
|
||||
fiatRate: BigDecimal? = null,
|
||||
) : this(
|
||||
amount = amount,
|
||||
fiatAmount = if (amount != null && fiatRate != null) {
|
||||
amount * fiatRate
|
||||
} else {
|
||||
null
|
||||
},
|
||||
fiatRate = fiatRate,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.common.ui.amountScreen.models
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class MaxEnterAmount(
|
||||
val amount: BigDecimal? = null,
|
||||
val fiatAmount: BigDecimal? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
)
|
||||
|
|
@ -138,7 +138,7 @@ private fun AmountFieldError(
|
|||
exit = fadeOut(),
|
||||
modifier = modifier,
|
||||
) {
|
||||
val errorText = remember(this) { error }
|
||||
val errorText = remember(this, error) { error }
|
||||
val color = if (isError) TangemTheme.colors.text.warning else TangemTheme.colors.text.attention
|
||||
Text(
|
||||
text = errorText.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package com.tangem.common.ui.amountScreen.utils
|
|||
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
|
|
@ -36,7 +36,10 @@ internal fun String.getFiatValue(
|
|||
}
|
||||
}
|
||||
|
||||
internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTextField: AmountFieldModel): Boolean {
|
||||
internal fun String.checkExceedBalance(
|
||||
maxEnterAmount: EnterAmountBoundary,
|
||||
amountTextField: AmountFieldModel,
|
||||
): Boolean {
|
||||
val currencyCryptoAmount = maxEnterAmount.amount ?: BigDecimal.ZERO
|
||||
val currencyFiatAmount = maxEnterAmount.fiatAmount ?: BigDecimal.ZERO
|
||||
val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals)
|
||||
|
|
@ -48,8 +51,8 @@ internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTex
|
|||
}
|
||||
}
|
||||
|
||||
internal fun getKeyboardAction(isExceedBalance: Boolean, decimalCryptoValue: BigDecimal) =
|
||||
if (!isExceedBalance && !decimalCryptoValue.isNullOrZero()) {
|
||||
internal fun getKeyboardAction(isCheckFailed: Boolean, decimalCryptoValue: BigDecimal) =
|
||||
if (!isCheckFailed && !decimalCryptoValue.isZero()) {
|
||||
ImeAction.Done
|
||||
} else {
|
||||
ImeAction.None
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@ sealed class NotificationUM(val config: NotificationConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class MinimumSendAmountError(val amount: String) : Error(
|
||||
title = resourceReference(R.string.send_notification_invalid_amount_title),
|
||||
subtitle = resourceReference(
|
||||
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
|
||||
wrappedList(amount, amount),
|
||||
),
|
||||
)
|
||||
|
||||
data class TransactionLimitError(
|
||||
val cryptoCurrency: String,
|
||||
val utxoLimit: String,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,20 @@ object NotificationsFactory {
|
|||
}
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addMinimumAmountErrorNotification(
|
||||
minimumSendAmount: BigDecimal?,
|
||||
sendingAmount: BigDecimal,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
if (minimumSendAmount != null && minimumSendAmount > sendingAmount) {
|
||||
add(
|
||||
NotificationUM.Error.MinimumSendAmountError(
|
||||
amount = minimumSendAmount.format { crypto(cryptoCurrency) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addTransactionLimitErrorNotification(
|
||||
utxoLimit: UtxoAmountLimit?,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
@ -179,7 +193,7 @@ object NotificationsFactory {
|
|||
if (isExceedsLimit) {
|
||||
add(
|
||||
NotificationUM.Error.MinimumAmountError(
|
||||
amount = dustValue.parseBigDecimal(cryptoCurrencyStatus.currency.decimals),
|
||||
amount = dustValue.format { crypto(cryptoCurrencyStatus.currency) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -287,7 +301,7 @@ object NotificationsFactory {
|
|||
dustValue?.let {
|
||||
add(
|
||||
NotificationUM.Error.MinimumAmountError(
|
||||
amount = it.parseBigDecimal(sendingCurrency.decimals),
|
||||
amount = it.format { crypto(sendingCurrency) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,8 +78,6 @@ sealed class Basic(
|
|||
error = error,
|
||||
)
|
||||
|
||||
class WalletOpened : Basic(event = "Wallet Opened")
|
||||
|
||||
class ButtonSupport(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Request Support",
|
||||
params = mapOf(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import java.math.BigDecimal
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class YieldDTO(
|
||||
@Json(name = "id")
|
||||
val id: String,
|
||||
val id: String?,
|
||||
@Json(name = "token")
|
||||
val token: TokenDTO?,
|
||||
@Json(name = "tokens")
|
||||
|
|
@ -67,9 +67,9 @@ data class YieldDTO(
|
|||
@Json(name = "address")
|
||||
val address: String?,
|
||||
@Json(name = "status")
|
||||
val status: ValidatorStatusDTO,
|
||||
val status: ValidatorStatusDTO?,
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
val name: String?,
|
||||
@Json(name = "image")
|
||||
val image: String?,
|
||||
@Json(name = "website")
|
||||
|
|
@ -83,7 +83,7 @@ data class YieldDTO(
|
|||
@Json(name = "votingPower")
|
||||
val votingPower: Double?,
|
||||
@Json(name = "preferred")
|
||||
val preferred: Boolean,
|
||||
val preferred: Boolean?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
enum class ValidatorStatusDTO {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ internal object StakingStoreModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingTokensStore(): StakingYieldsStore {
|
||||
return DefaultStakingYieldsStore()
|
||||
return DefaultStakingYieldsStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -1,16 +1,32 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
internal class DefaultStakingYieldsStore : StakingYieldsStore {
|
||||
internal class DefaultStakingYieldsStore(
|
||||
private val dataStore: StringKeyDataStore<List<YieldDTO>>,
|
||||
) : StakingYieldsStore {
|
||||
|
||||
private var yields = listOf<YieldDTO>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
override fun get(): List<YieldDTO> {
|
||||
return yields
|
||||
override fun get(): Flow<List<YieldDTO>> {
|
||||
return dataStore.get(KEY)
|
||||
}
|
||||
|
||||
override fun store(items: List<YieldDTO>) {
|
||||
yields = items
|
||||
override suspend fun getSync(): List<YieldDTO> {
|
||||
return dataStore.getSyncOrNull(KEY) ?: emptyList()
|
||||
}
|
||||
|
||||
override suspend fun store(items: List<YieldDTO>) {
|
||||
mutex.withLock {
|
||||
dataStore.store(KEY, items)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY = "DefaultStakingYieldsStore"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface StakingYieldsStore {
|
||||
|
||||
fun get(): List<YieldDTO>
|
||||
fun get(): Flow<List<YieldDTO>>
|
||||
|
||||
fun store(items: List<YieldDTO>)
|
||||
suspend fun getSync(): List<YieldDTO>
|
||||
|
||||
suspend fun store(items: List<YieldDTO>)
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es zu finden und zum Kauf hinzuzufügen</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es zu finden und zum Verkauf hinzuzufügen</string>
|
||||
<string name="action_buttons_swap_choose_token">Wähle den Token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es für Swaps zu finden und hinzuzufügen</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">Es gibt keine verfügbaren Token, die mit dem ausgewählten Token getauscht werden können. Bitte wähle einen anderen.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">Kein verfügbares Paar</string>
|
||||
<string name="action_buttons_you_want_to_receive">Du möchtest erhalten</string>
|
||||
<string name="action_buttons_you_want_to_swap">Du möchtest tauschen</string>
|
||||
<string name="add_custom_token_choose_network">Netzwerk wählen</string>
|
||||
|
|
@ -257,7 +262,7 @@
|
|||
<string name="express_exchange_status_canceled">Abgebrochen</string>
|
||||
<string name="express_exchange_status_confirmed">Bestätigt</string>
|
||||
<string name="express_exchange_status_confirming">Bestätigen</string>
|
||||
<string name="express_exchange_status_confirming_active">wird bestätigt...</string>
|
||||
<string name="express_exchange_status_confirming_active">Wird bestätigt...</string>
|
||||
<string name="express_exchange_status_exchanged">Getauscht</string>
|
||||
<string name="express_exchange_status_exchanging">Tauschen läuft</string>
|
||||
<string name="express_exchange_status_exchanging_active">Austauschen...</string>
|
||||
|
|
@ -448,6 +453,7 @@
|
|||
<string name="markets_token_details_price_performance">Preisleistung</string>
|
||||
<string name="markets_token_details_repository">Aufbewahrungsort</string>
|
||||
<string name="markets_token_details_security_score">Sicherheitsbewertung</string>
|
||||
<string name="markets_token_details_security_score_description">Der Security Score eines Tokens ist eine Metrik, die das Sicherheitsniveau einer Blockchain oder eines Tokens anhand verschiedener Faktoren bewertet und aus den unten aufgeführten Quellen zusammengestellt wird.</string>
|
||||
<string name="markets_token_details_social">Soziales</string>
|
||||
<string name="markets_token_details_total_supply">Gesamtangebot</string>
|
||||
<string name="markets_token_details_total_supply_description">Die maximale Anzahl von Coins oder Tokens, die jemals für eine bestimmte Kryptowährung existieren können</string>
|
||||
|
|
@ -554,10 +560,21 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Zugangscode wiederherstellen</string>
|
||||
<string name="onboarding_wallet_info_title_second">Identische Karten oder Ring</string>
|
||||
<string name="onboarding_wallet_info_title_third">Zugangscode</string>
|
||||
<string name="onramp_country_search">Suche nach Land</string>
|
||||
<string name="onramp_country_unavailable">Nicht verfügbar</string>
|
||||
<string name="onramp_currency_search">Suche nach Währung</string>
|
||||
<string name="onramp_max_amount_restriction">Der Kaufbetrag sollte nicht höher sein als %s</string>
|
||||
<string name="onramp_min_amount_restriction">Der zu kaufende Betrag muss mindestens %s betragen</string>
|
||||
<string name="onramp_no_available_providers">Keine verfügbaren Anbieter für diese Währung</string>
|
||||
<string name="onramp_pay_with">Bezahlen mit</string>
|
||||
<string name="onramp_redirecting_to_provider_subtitle">Du kannst Deine Transaktion beim Drittanbieter %s abschließen.</string>
|
||||
<string name="onramp_redirecting_to_provider_title">Umleitung auf %s...</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Unsere Dienstleistungen sind in diesem Land nicht verfügbar</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">Änder oder bestätige bitte</string>
|
||||
<string name="onramp_residency_bottomsheet_title">Dein Wohnsitz wurde identifiziert als</string>
|
||||
<string name="onramp_settings_residence">Residenz</string>
|
||||
<string name="onramp_settings_residence_description">Bitte wähle das richtige Land aus, um korrekte Zahlungsoptionen und Dienstleistungen zu gewährleisten.</string>
|
||||
<string name="onramp_settings_title">Einstellungen</string>
|
||||
<string name="onramp_via">Über</string>
|
||||
<string name="organize_tokens_group">Gruppe erstellen</string>
|
||||
<string name="organize_tokens_sort_by_balance">Nach Guthaben</string>
|
||||
|
|
@ -661,7 +678,7 @@
|
|||
<string name="send_notification_high_fee_text">Aufgrund der Besonderheiten des Netzes %1$s ist die Gebühr für die Überweisung des gesamten Guthabens höher. Um die Kommission zu reduzieren, Kannst du %2$s verlassen.</string>
|
||||
<string name="send_notification_high_fee_title">Die Gebühr ist höher</string>
|
||||
<string name="send_notification_invalid_amount_text">Die enthaltene Kommission übersteigt den Überweisungsbetrag, was zu einem negativen Wert führt</string>
|
||||
<string name="send_notification_invalid_amount_title">ungültige Menge</string>
|
||||
<string name="send_notification_invalid_amount_title">Ungültige Menge</string>
|
||||
<string name="send_notification_invalid_minimum_amount_text">Der Mindestbetrag für den Versand beträgt %1$s. Bitte stell sicher, dass der Restbetrag nach dem Versand nicht unter %2$s liegt.</string>
|
||||
<string name="send_notification_invalid_reserve_amount_text">Das Zielkonto wurde nicht erstellt. Bitte änder den zu sendenden Betrag.</string>
|
||||
<string name="send_notification_invalid_reserve_amount_title">Der zu sendende Betrag muss mindestens %s betragen</string>
|
||||
|
|
@ -744,6 +761,8 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Woche.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Sicher staken und wöchentliche Belohnungen verdienen.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Verdiene Staking-Belohnungen</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Dein verbleibender Stakingbetrag ist zu niedrig, um den Einsatz aufzuheben. Du musst mehr Staken, um den Mindestbetrag zum Aufheben des Einsatzes zu erreichen.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Niedriges Stakingguthaben</string>
|
||||
<string name="staking_notification_network_error_text">Aufgrund von Netzwerkproblemen ist Staking derzeit nicht verfügbar. Bitte versuche es später erneut.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Beim Staking im %1$s -Netzwerk mit einem neuen Validator werden alle zuvor eingesetzten Kryptos automatisch an diesen Validator übertragen</string>
|
||||
<string name="staking_notification_restake_rewards_text">Reinvestiert Deine verdienten Prämien in Deinen Einsatzbetrag und erhöht so den potenziellen Gewinn.</string>
|
||||
|
|
@ -788,7 +807,7 @@
|
|||
<string name="staking_unbonding">Lösen der Bindungen</string>
|
||||
<string name="staking_unlocked_locked">Gelocktes unlocken</string>
|
||||
<string name="staking_unlocking">Entsperren</string>
|
||||
<string name="staking_unstake_amount_requirement_error">Die Anzahl der zu stakenden Krypros muss mindesten %s betragen</string>
|
||||
<string name="staking_unstake_amount_requirement_error">Der unstaking-Betrag muss mindestens %s betragen</string>
|
||||
<string name="staking_unstake_amount_validation_error">Der Betrag übersteigt das eingesetzte Guthaben</string>
|
||||
<string name="staking_unstaked">Unstaken</string>
|
||||
<string name="staking_unstaking">Staking beenden</string>
|
||||
|
|
@ -815,9 +834,10 @@
|
|||
<string name="swap_promo_text">Tausche mehr Token zu besseren Kursen direkt in deiner Brieftasche.</string>
|
||||
<string name="swap_promo_title">Neuer Swap-Anbieter verfügbar!</string>
|
||||
<string name="swapping_alert_cex_description">Der Betrag umfasst:\n- Gebühr des Dienstanbieters\n- Netzgebühr für die Rücksendung von %s von der Vermittlungsstelle an die Adresse des Nutzers.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">Der Betrag beinhaltet:\n• Honorar des Dienstleisters\n• Netzwerkgebühr für das Senden %1$s von der Börse zurück an die Adresse des Benutzers. \n\n Provider-Slippage kann bis zu %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Der Betrag enthält die Gebühren des Dienstleisters.</string>
|
||||
<string name="swapping_alert_slippage_description">Anbieter-Slippage kann bis zu %s ausfallen.</string>
|
||||
<string name="swapping_alert_title">Gebühren</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">Der Betrag beinhaltet die Gebühr des Dienstleisters. \n\nProvider-Slippage kann bis zu %s</string>
|
||||
<string name="swapping_alert_title">Information</string>
|
||||
<string name="swapping_approve_information_text">Alle dezentralen Börsen benötigen Genehmigungen, um zu verhindern, dass intelligente Verträge ohne Ihre Erlaubnis auf Ihre Geldbörse zugreifen. Smart Contracts können nicht auf Ihre Token zugreifen, wenn Sie nicht zustimmen. Indem Sie Ihre Token \"freischalten\", ermächtigen Sie den 1-Zoll-Smart-Contract, sie auszugeben. Die Miner des Netzwerks erhalten eine (von Ihnen bezahlte) Gasgebühr, um diese Aktion in der Blockchain aufzuzeichnen. Sie können Ihre Token tauschen, nachdem Sie Ihre Zustimmung gegeben haben.</string>
|
||||
<string name="swapping_approve_information_title">Genehmigen</string>
|
||||
<string name="swapping_fee_estimation_error_text">Fehler bei der Gebührenschätzung. Bitte sende dein Feedback an den Support.</string>
|
||||
|
|
@ -867,6 +887,8 @@
|
|||
<string name="transaction_history_transaction_from_address">von: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">zu: %s</string>
|
||||
<string name="transaction_history_transaction_validator">Validierer: %s</string>
|
||||
<string name="transfer_min_amount_error">Minimum %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">Der Mindesttransaktionsbetrag beträgt %1$s.</string>
|
||||
<string name="try_to_load_data_again_button_title">Versuche es erneut</string>
|
||||
<string name="twin_error_same_card">Du hast dieselbe Karte oder Ring gescannt. Um ein Zwillings-Wallet zu erstellen, musst du die Karte oder Ring mit der Nummer %d scannen.</string>
|
||||
<string name="twin_error_wrong_twin">Du hast die falsche Doppelkarte oder Ring gescannt. Bitte versuche eine andere Karte oder Ring</string>
|
||||
|
|
@ -1002,7 +1024,7 @@
|
|||
<string name="warning_solana_fee_message">Das Solana-Netz ist überlastet. Wenn deine Transaktion nicht innerhalb von 2 Minuten bearbeitet wird, wiederhole bitte die Transaktion.</string>
|
||||
<string name="warning_solana_fee_title">Solana Netzwerkalarm</string>
|
||||
<string name="warning_solana_rent_fee_message">Das Solana-Netzwerk erhebt alle 2 Tage eine Miete von %1$s. Konten, die sich die Miete nicht leisten können, werden aus dem Netzwerk gelöscht. Hinterlege deinem Konto mit mehr als %2$s, um es kostenlos zu nutzen.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Einige Netzwerke sind derzeit nicht erreichbar. Bitte versuche es später erneut.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Wischen Sie nach unten, um zu aktualisieren, oder versuchen Sie es später erneut.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Einige Netzwerke sind nicht erreichbar</string>
|
||||
<string name="warning_testnet_card_message">Dies ist eine Testnet-Karte. Sie kann keine Transaktionen verarbeiten und sollte nur zu Test- und Entwicklungszwecken verwendet werden.</string>
|
||||
<string name="warning_testnet_card_title">Nur für Testzwecke</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">¿No encuentra el token en su billetera? Consulte los mercados para encontrarlo y añadirlo a la compra</string>
|
||||
<string name="action_buttons_sell_empty_search_message">¿No encuentra el token en su billetera? Consulte los mercados para encontrarlo y añadirlo a la venta</string>
|
||||
<string name="action_buttons_swap_choose_token">Elige el token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">¿No encuentras el token en tu billetera? Consulte los mercados para encontrarlo y agregarlo al intercambio</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">No hay tokens disponibles para intercambiar con el token seleccionado. Por favor elige otro.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">No hay par disponible</string>
|
||||
<string name="action_buttons_you_want_to_receive">Desea recibir</string>
|
||||
<string name="action_buttons_you_want_to_swap">Quiere intercambiar</string>
|
||||
<string name="add_custom_token_choose_network">Elige red</string>
|
||||
|
|
@ -91,7 +96,7 @@
|
|||
<string name="common_claim">Reclamar</string>
|
||||
<string name="common_claim_rewards">Reclame recompensas</string>
|
||||
<string name="common_close">Cerrar</string>
|
||||
<string name="common_confirm">confirme</string>
|
||||
<string name="common_confirm">Confirme</string>
|
||||
<string name="common_continue">Continuar</string>
|
||||
<string name="common_copy">Copiar</string>
|
||||
<string name="common_copy_address">Copiar la dirección</string>
|
||||
|
|
@ -554,9 +559,20 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Restaurar código de acceso</string>
|
||||
<string name="onboarding_wallet_info_title_second">Tarjetas idénticas</string>
|
||||
<string name="onboarding_wallet_info_title_third">Código de acceso</string>
|
||||
<string name="onramp_country_search">Buscar por país</string>
|
||||
<string name="onramp_country_unavailable">Indisponible</string>
|
||||
<string name="onramp_currency_search">Buscar por moneda</string>
|
||||
<string name="onramp_max_amount_restriction">El monto de la compra no debe ser mayor a %s</string>
|
||||
<string name="onramp_min_amount_restriction">La cantidad a comprar debe ser como mínimo %s</string>
|
||||
<string name="onramp_no_available_providers">No hay proveedores disponibles para esta moneda</string>
|
||||
<string name="onramp_pay_with">Pagar con</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Nuestros servicios no están disponibles en este país</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">Cámbielo o confírmelo</string>
|
||||
<string name="onramp_residency_bottomsheet_title">Su residencia ha sido identificada como</string>
|
||||
<string name="onramp_settings_residence">Residencia</string>
|
||||
<string name="onramp_settings_residence_description">Seleccione el país correcto para garantizar opciones de pago y servicios precisos.</string>
|
||||
<string name="onramp_settings_title">Ajustes</string>
|
||||
<string name="onramp_via">Vía</string>
|
||||
<string name="organize_tokens_group">Agrupar</string>
|
||||
<string name="organize_tokens_sort_by_balance">Por saldo</string>
|
||||
<string name="organize_tokens_title">Organizar tokens</string>
|
||||
|
|
@ -699,6 +715,7 @@
|
|||
<string name="settings_wallet_name_title">Nombre</string>
|
||||
<string name="staking_amount_requirement_error">El monto del staking debe ser al menos %s</string>
|
||||
<string name="staking_amount_tron_integer_error">El monto del staking se redondeará a %1$s TRX debido a las reglas de la red.</string>
|
||||
<string name="staking_amount_tron_integer_error_unstaking">El monto de cancelación del staking se redondeará a %1$s TRX debido a las reglas de la red.</string>
|
||||
<string name="staking_claim_unstaked">Unstaking de la reclamación</string>
|
||||
<string name="staking_details_account_fee">Tarifa de staking de la cuenta </string>
|
||||
<string name="staking_details_account_fee_info">Una cuenta de staking es una cuenta especial donde se almacenan los tokens SOL de staking. Se crea cuando delegas sus tokens a un validador para participar en la validación de transacciones y ganar recompensas. Se cobra una pequeña tarifa por crear la cuenta de staking, que se devuelve una vez que se completa el staking.</string>
|
||||
|
|
@ -725,7 +742,7 @@
|
|||
<string name="staking_details_warmup_period">Periodo de calentamiento</string>
|
||||
<string name="staking_details_warmup_period_info">El tiempo permitido para activar la participación en la apuesta.</string>
|
||||
<string name="staking_give_permission_fee_footer">La red cobrará una tarifa de aprobación de token para verificar que usted está autorizando el uso de su token para el staking.</string>
|
||||
<string name="staking_legal">Al utilizar la función de staking, usted acepta %1$s y %2$s del proveedor</string>
|
||||
<string name="staking_legal">Al utilizar la función de staking, usted acepta %1$s y %2$s del proveedor</string>
|
||||
<string name="staking_locked">Bloqueado</string>
|
||||
<string name="staking_migrate">Migrar</string>
|
||||
<string name="staking_native">Native staking</string>
|
||||
|
|
@ -741,6 +758,8 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">El staking le permite ganar %1$s. Sus recompensas por apostar llegan todas las semanas.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Haga staking de forma segura y comience a ganar recompensas semanales</string>
|
||||
<string name="staking_notification_earn_rewards_title">Gane recompensas por staking</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Su saldo de staking será demasiado bajo para cancelar el staking. Necesitará hacer staking más para alcanzar la cantidad mínima para cancelarlo.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Saldo de staking bajo</string>
|
||||
<string name="staking_notification_network_error_text">El staking no está disponible actualmente debido a las condiciones de la red. Inténtelo de nuevo más tarde.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">El staking en la red %1$s con un nuevo validador transferirá automáticamente todos los fondos previamente en staking a este validador.</string>
|
||||
<string name="staking_notification_restake_rewards_text">Reinvierta las recompensas obtenidas en el monto apostado, aumentando las ganancias potenciales.</string>
|
||||
|
|
@ -774,6 +793,7 @@
|
|||
<string name="staking_rewards">Recompensas</string>
|
||||
<string name="staking_stake_locked">El stake está bloqueado</string>
|
||||
<string name="staking_stake_more">Hacer más staking</string>
|
||||
<string name="staking_staked_amount">Monto del staking</string>
|
||||
<string name="staking_summary_description_text">Ud hace el staking de %1$s y recibirá %2$s</string>
|
||||
<string name="staking_tap_to_unlock">Toque para desbloquear</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Pulse para desbloquear o votar</string>
|
||||
|
|
@ -784,7 +804,7 @@
|
|||
<string name="staking_unbonding">Desunión</string>
|
||||
<string name="staking_unlocked_locked">Desbloquear</string>
|
||||
<string name="staking_unlocking">Desbloqueando</string>
|
||||
<string name="staking_unstake_amount_requirement_error">El monto del staking debe ser al menos %s</string>
|
||||
<string name="staking_unstake_amount_requirement_error">La cantidad para el staking debe ser al menos %s</string>
|
||||
<string name="staking_unstake_amount_validation_error">El monto excede el saldo apostado</string>
|
||||
<string name="staking_unstaked">Sin staking</string>
|
||||
<string name="staking_unstaking">Unstaking</string>
|
||||
|
|
@ -811,7 +831,9 @@
|
|||
<string name="swap_promo_text">Intercambie más tokens a mejores tasas directamente en su billetera.</string>
|
||||
<string name="swap_promo_title">¡Nuevo proveedor de intercambio disponible!</string>
|
||||
<string name="swapping_alert_cex_description">El monto incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">El monto incluye:\n• tarifas del proveedor de servicios\n• tarifas de red por enviar %1$s desde el intercambio a la dirección del usuario. \n\nEl slippage del proveedor puede alcanzar el %2$s</string>
|
||||
<string name="swapping_alert_dex_description">El importe incluye los honorarios del proveedor de servicios.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">El importe incluye las tarifas del proveedor de servicios. \n\nEl slippage del proveedor puede alcanzar el %s</string>
|
||||
<string name="swapping_alert_title">Tarifa</string>
|
||||
<string name="swapping_approve_information_text">Todos los exchanges descentralizados requieren aprobaciones para evitar que los smart contracts accedan a su billetera sin su permiso. Por diseño, los smart contracts no pueden acceder a tus tokens a menos que lo apruebes. Al \"desbloquear\" sus tokens, autoriza al smart contract de 1-inch a gastarlos. Los mineros de la red reciben una tarifa de gas (pagada por voz) para registrar esta acción en la blockchain. Puede intercambiar su token después de dar la aprobación.</string>
|
||||
<string name="swapping_approve_information_title">Aprobar</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à l\'achat</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à la vente</string>
|
||||
<string name="action_buttons_swap_choose_token">Choisissez le jeton</string>
|
||||
<string name="action_buttons_swap_empty_search_message">Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à l\'échange</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">Il n\'y a pas de token disponible pour échanger avec le token sélectionné. Veuillez en choisir un autre.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">Aucune paire disponible</string>
|
||||
<string name="action_buttons_you_want_to_receive">Vous souhaitez recevoir</string>
|
||||
<string name="action_buttons_you_want_to_swap">Vous souhaitez échanger</string>
|
||||
<string name="add_custom_token_choose_network">Choisissez le réseau</string>
|
||||
|
|
@ -554,9 +559,20 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Restauration du code d\'accès</string>
|
||||
<string name="onboarding_wallet_info_title_second">Cartes identiques</string>
|
||||
<string name="onboarding_wallet_info_title_third">Code d\'accès</string>
|
||||
<string name="onramp_country_search">Recherche par pays</string>
|
||||
<string name="onramp_country_unavailable">Indisponible</string>
|
||||
<string name="onramp_currency_search">Recherche par devise</string>
|
||||
<string name="onramp_max_amount_restriction">Le montant de l\'achat ne doit pas dépasser %s</string>
|
||||
<string name="onramp_min_amount_restriction">Le montant à acheter doit être au moins %s</string>
|
||||
<string name="onramp_no_available_providers">Aucun fournisseur disponible pour cette devise</string>
|
||||
<string name="onramp_pay_with">Payer avec</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Nos services ne sont pas disponibles dans ce pays</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">Modifiez-le ou confirmez-le</string>
|
||||
<string name="onramp_residency_bottomsheet_title">Votre résidence a été identifiée comme</string>
|
||||
<string name="onramp_settings_residence">Résidence</string>
|
||||
<string name="onramp_settings_residence_description">Veuillez sélectionner le bon pays pour garantir des options de paiement et des services précis.</string>
|
||||
<string name="onramp_settings_title">Paramètres</string>
|
||||
<string name="onramp_via">Via</string>
|
||||
<string name="organize_tokens_group">Grouper</string>
|
||||
<string name="organize_tokens_sort_by_balance">Par solde</string>
|
||||
<string name="organize_tokens_title">Organiser les jetons</string>
|
||||
|
|
@ -699,6 +715,7 @@
|
|||
<string name="settings_wallet_name_title">Nom</string>
|
||||
<string name="staking_amount_requirement_error">Le montant à staker doit être au moins %s</string>
|
||||
<string name="staking_amount_tron_integer_error">Le montant du staking sera arrondi à %1$s TRX en raison des règles du réseau.</string>
|
||||
<string name="staking_amount_tron_integer_error_unstaking">Le montant d\'annulation du staking sera arrondi à %1$s TRX en raison des règles du réseau.</string>
|
||||
<string name="staking_claim_unstaked">Réclamation déstakée</string>
|
||||
<string name="staking_details_account_fee">Frais de staking du compte</string>
|
||||
<string name="staking_details_account_fee_info">Un compte de staking est un compte spécial où sont stockés les jetons SOL stakés. Il est créé lorsque vous déléguez vos jetons à un validateur pour participer à la validation des transactions et gagner des récompenses. Des frais minimes sont facturés pour la création du compte de staking, qui sont restitués une fois le staking terminé.</string>
|
||||
|
|
@ -725,7 +742,7 @@
|
|||
<string name="staking_details_warmup_period">Période d\'échauffement</string>
|
||||
<string name="staking_details_warmup_period_info">Le temps imparti pour activer la participation au staking.</string>
|
||||
<string name="staking_give_permission_fee_footer">Le réseau facturera des frais d’approbation de jeton pour vérifier que vous autorisez l’utilisation de votre jeton pour le jalonnement.</string>
|
||||
<string name="staking_legal">En utilisant la fonctionnalité de staking, vous acceptez les %1$s et %2$s du fournisseur</string>
|
||||
<string name="staking_legal">En utilisant la fonctionnalité de staking, vous acceptez les %1$s et %2$s du fournisseur</string>
|
||||
<string name="staking_locked">Bloqué</string>
|
||||
<string name="staking_migrate">Migrer</string>
|
||||
<string name="staking_native">Native staking</string>
|
||||
|
|
@ -741,6 +758,8 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">Le staking vous permet de gagner %1$s. Vos récompenses de staking arrivent toutes les semaines.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Stakez en toute sécurité et commencez à gagner des récompenses hebdomadaires</string>
|
||||
<string name="staking_notification_earn_rewards_title">Gagnez des récompenses de staking</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Votre solde stakée sera trop faible pour annuler votre staking. Vous devrez staker davantage pour atteindre le montant minimum pour annuler le staking.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Solde de staking faible</string>
|
||||
<string name="staking_notification_network_error_text">L\'option de staking n\'est actuellement pas disponible en raison des conditions du réseau. Veuillez réessayer plus tard.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Le staking dans le réseau %1$s avec un nouveau validateur transférera automatiquement tous les fonds précédemment stakés vers ce validateur</string>
|
||||
<string name="staking_notification_restake_rewards_text">Réinvestissez vos récompenses gagnées dans le montant que vous avez staké, augmentant ainsi vos gains potentiels.</string>
|
||||
|
|
@ -774,6 +793,7 @@
|
|||
<string name="staking_rewards">Récompenses</string>
|
||||
<string name="staking_stake_locked">Stake verrouillé</string>
|
||||
<string name="staking_stake_more">Staker plus</string>
|
||||
<string name="staking_staked_amount">Montant staké</string>
|
||||
<string name="staking_summary_description_text">Vous stakez %1$s et recevrez %2$s</string>
|
||||
<string name="staking_tap_to_unlock">Appuyez pour déverrouiller</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Appuyez pour déverrouiller ou voter</string>
|
||||
|
|
@ -784,7 +804,7 @@
|
|||
<string name="staking_unbonding">Dissociation</string>
|
||||
<string name="staking_unlocked_locked">Débloquer</string>
|
||||
<string name="staking_unlocking">Déverrouillage</string>
|
||||
<string name="staking_unstake_amount_requirement_error">Le montant à staker doit être au moins %s</string>
|
||||
<string name="staking_unstake_amount_requirement_error">Le montant à destaker doit être au moins %s</string>
|
||||
<string name="staking_unstake_amount_validation_error">Le montant dépasse le solde misé</string>
|
||||
<string name="staking_unstaked">Non-staké</string>
|
||||
<string name="staking_unstaking">Unstaking</string>
|
||||
|
|
@ -811,7 +831,9 @@
|
|||
<string name="swap_promo_text">Échangez plus de jetons à de meilleurs taux directement dans votre portefeuille.</string>
|
||||
<string name="swap_promo_title">Nouveau fournisseur d\'échange disponible !</string>
|
||||
<string name="swapping_alert_cex_description">Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %s depuis l\'échange vers l\'adresse de l\'utilisateur.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %1$s depuis l\'échange vers l\'adresse de l\'utilisateur. \n\nLe slippage du fournisseur peut atteindre %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Le montant comprend les frais du fournisseur de services.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">Le montant comprend les frais du fournisseur de services. \n\nLe slippage du fournisseur peut atteindre %s</string>
|
||||
<string name="swapping_alert_title">Frais</string>
|
||||
<string name="swapping_approve_information_text">Tous les échanges décentralisés nécessitent des approbations pour empêcher les smart contracts d\'accéder à votre portefeuille sans votre permission. Par conception, les smart contracts ne peuvent pas accéder à vos jetons sans votre approbation. En « déverrouillant » vos jetons, vous autorisez le smart contract 1-inch à les dépenser. Les mineurs du réseau reçoivent des frais de gaz (payés par vous) pour enregistrer cette action sur la blockchain. Vous pouvez échanger votre jeton après avoir donné votre approbation.</string>
|
||||
<string name="swapping_approve_information_title">Approuver</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、買付のために追加してください</string>
|
||||
<string name="action_buttons_sell_empty_search_message">ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、売却のために追加してください</string>
|
||||
<string name="action_buttons_swap_choose_token">トークンを選択</string>
|
||||
<string name="action_buttons_swap_empty_search_message">ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、スワップのために追加してください</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">選択したトークンと交換できるトークンがありません。別のトークンを選択してください。</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">利用可能なペアがありません</string>
|
||||
<string name="action_buttons_you_want_to_receive">受け取りたい</string>
|
||||
<string name="action_buttons_you_want_to_swap">交換したい</string>
|
||||
<string name="add_custom_token_choose_network">ネットワークを選択</string>
|
||||
|
|
@ -442,6 +447,7 @@
|
|||
<string name="markets_token_details_price_performance">値動き</string>
|
||||
<string name="markets_token_details_repository">リポジトリ</string>
|
||||
<string name="markets_token_details_security_score">セキュリティ・スコア</string>
|
||||
<string name="markets_token_details_security_score_description">トークンのセキュリティ・スコアとは、ブロックチェーンやトークンのセキュリティ・レベルを様々な要因に基づいて評価する指標で、以下の情報源から集計されます。</string>
|
||||
<string name="markets_token_details_social">ソーシャル</string>
|
||||
<string name="markets_token_details_total_supply">総供給量</string>
|
||||
<string name="markets_token_details_total_supply_description">特定の暗号資産に存在しうるコインまたはトークンの最大数</string>
|
||||
|
|
@ -546,10 +552,21 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">アクセスコードの復元</string>
|
||||
<string name="onboarding_wallet_info_title_second">同一のカード</string>
|
||||
<string name="onboarding_wallet_info_title_third">アクセスコード</string>
|
||||
<string name="onramp_country_search">国で検索</string>
|
||||
<string name="onramp_country_unavailable">利用不可</string>
|
||||
<string name="onramp_currency_search">通貨で検索</string>
|
||||
<string name="onramp_max_amount_restriction">買付金額は%s以下にしてください</string>
|
||||
<string name="onramp_min_amount_restriction">買付金額は少なくとも%sである必要があります</string>
|
||||
<string name="onramp_no_available_providers">この通貨で利用可能なプロバイダーはありません</string>
|
||||
<string name="onramp_pay_with">支払う</string>
|
||||
<string name="onramp_redirecting_to_provider_subtitle">サードパーティプロバイダー%sで取引を完了できます。</string>
|
||||
<string name="onramp_redirecting_to_provider_title">%sにリダイレクトしています...</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">この国では当社のサービスはご利用いただけません</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">変更または確認</string>
|
||||
<string name="onramp_residency_bottomsheet_title">あなたの住居は次のように識別されています</string>
|
||||
<string name="onramp_settings_residence">住居</string>
|
||||
<string name="onramp_settings_residence_description">正確なお支払い方法とサービスを確保するため、正しい国を選択してください。</string>
|
||||
<string name="onramp_settings_title">設定</string>
|
||||
<string name="onramp_via">経由</string>
|
||||
<string name="organize_tokens_group">グループ</string>
|
||||
<string name="organize_tokens_sort_by_balance">残高順</string>
|
||||
|
|
@ -734,6 +751,8 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">安全にステーキングして、報酬を毎週獲得しましょう</string>
|
||||
<string name="staking_notification_earn_rewards_title">ステーキング報酬を獲得</string>
|
||||
<string name="staking_notification_low_staked_balance_text">残りのステーキング残高が少なすぎてステーキングを解除できません。最小のステーキング解除残高を満たすには、さらにステーキングする必要があります。</string>
|
||||
<string name="staking_notification_low_staked_balance_title">ステーキング残高が低いです</string>
|
||||
<string name="staking_notification_network_error_text">ネットワークの状態により、現在ステーキングはご利用いただけません。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">新しいバリデーターで%1$sネットワークにステーキングすると、以前にステーキングされた資金はすべてこのバリデーターに自動的に転送されます。</string>
|
||||
<string name="staking_notification_restake_rewards_text">獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。</string>
|
||||
|
|
@ -778,7 +797,7 @@
|
|||
<string name="staking_unbonding">ステーキング解約中</string>
|
||||
<string name="staking_unlocked_locked">ロック解除</string>
|
||||
<string name="staking_unlocking">ロック解除中</string>
|
||||
<string name="staking_unstake_amount_requirement_error">ステーキング金額は %s 以上である必要があります</string>
|
||||
<string name="staking_unstake_amount_requirement_error">ステーキング解除する金額は少なくとも%sである必要があります</string>
|
||||
<string name="staking_unstake_amount_validation_error">金額がステーキング残高を超えています</string>
|
||||
<string name="staking_unstaked">ステーキングされていない</string>
|
||||
<string name="staking_unstaking">ステーキング解除</string>
|
||||
|
|
@ -805,8 +824,9 @@
|
|||
<string name="swap_promo_text">より多くのトークンをより良いレートで、ウォレット内にて直接交換します。</string>
|
||||
<string name="swap_promo_title">新しいスワッププロバイダーが利用可能になりました!</string>
|
||||
<string name="swapping_alert_cex_description">この金額には以下が含まれます:\n- サービスプロバイダーの手数料\n- 取引所からユーザーのアドレスに%s を送り返すためのネットワーク手数料。</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです</string>
|
||||
<string name="swapping_alert_dex_description">この金額には、サービスプロバイダーの手数料が含まれています。</string>
|
||||
<string name="swapping_alert_slippage_description">プロバイダーのスリッページは最大%sです。</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です</string>
|
||||
<string name="swapping_alert_title">手数料</string>
|
||||
<string name="swapping_approve_information_text">すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。</string>
|
||||
<string name="swapping_approve_information_title">承認</string>
|
||||
|
|
@ -857,6 +877,8 @@
|
|||
<string name="transaction_history_transaction_from_address">送金元: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">送金先: %s</string>
|
||||
<string name="transaction_history_transaction_validator">バリデーター: %s</string>
|
||||
<string name="transfer_min_amount_error">最小%s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">最小取引金額は%1$sです。</string>
|
||||
<string name="try_to_load_data_again_button_title">もう一度やり直してください</string>
|
||||
<string name="twin_error_same_card">同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。</string>
|
||||
<string name="twin_error_wrong_twin">間違ったツインカードをスキャンしました。別のカードをお試しください。</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_swap_choose_token">Выберите токен</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">Нет доступных токенов для обмена с выбранным токеном, пожалуйста, выберите другой.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">Нет доступных пар</string>
|
||||
<string name="action_buttons_you_want_to_receive">Вы хотите получить</string>
|
||||
<string name="action_buttons_you_want_to_swap">Вы хотите обменять</string>
|
||||
<string name="add_custom_token_choose_network">Выберите сеть</string>
|
||||
|
|
@ -311,7 +313,7 @@
|
|||
<string name="give_permission_current_transaction">Транзакция</string>
|
||||
<string name="give_permission_policy_type_footer">Укажите лимит доступа к выбранному токену</string>
|
||||
<string name="give_permission_rows_amount">Количество %s</string>
|
||||
<string name="give_permission_staking_footer">Функция подтверждения необходима для предоставления другому адресу разрешения на использование определенного количества ваших токенов.По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту StakeKit разрешение использовать ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете осуществить стейкинг токена.</string>
|
||||
<string name="give_permission_staking_footer">Функция подтверждения необходима для предоставления другому адресу разрешения на использование определенного количества ваших токенов. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту StakeKit разрешение использовать ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете осуществить стейкинг токена.</string>
|
||||
<string name="give_permission_staking_subtitle">Чтобы продолжить, вам необходимо разрешить смарт контракту Polygon использовать ваш %s</string>
|
||||
<string name="give_permission_swap_subtitle" formatted="false">Чтобы продолжить, вам нужно разрешить смарт-контракту %1s использовать ваш %2s</string>
|
||||
<string name="give_permission_title">Дать разрешение</string>
|
||||
|
|
@ -569,7 +571,14 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Восстановление кода доступа</string>
|
||||
<string name="onboarding_wallet_info_title_second">Идентичные карты</string>
|
||||
<string name="onboarding_wallet_info_title_third">Код доступа</string>
|
||||
<string name="onramp_country_search">Поиск по стране</string>
|
||||
<string name="onramp_country_unavailable">Недоступно</string>
|
||||
<string name="onramp_currency_search">Поиск по валюте</string>
|
||||
<string name="onramp_max_amount_restriction">Сумма покупки не может быть больше, чем %s</string>
|
||||
<string name="onramp_min_amount_restriction">Сумма покупки должна составлять минимум %s</string>
|
||||
<string name="onramp_pay_with">Оплата с</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Наши сервисы недоступны в данной стране</string>
|
||||
<string name="onramp_settings_title">Настройки</string>
|
||||
<string name="onramp_via">Через</string>
|
||||
<string name="organize_tokens_group">Группы</string>
|
||||
<string name="organize_tokens_sort_by_balance">По балансу</string>
|
||||
|
|
@ -761,6 +770,7 @@
|
|||
<string name="staking_notification_earn_rewards_text_weekly">Стейкайте безопасно и начинайте получать еженедельные награды.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Получите награду за стейкинг</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Оставшийся застейканный баланс будет слишком мал для вывода. Вам потребуется застейкать больше средств, чтобы достичь минимальной суммы для вывода.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Низкий баланс стейкинга</string>
|
||||
<string name="staking_notification_network_error_text">Стейкинг временно недоступен из-за проблем в сети. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Стейкинг в сети %1$s с новым валидатором автоматически переведет ваши текущие застейканные средства на него.</string>
|
||||
<string name="staking_notification_restake_rewards_text">Реинвестируйте свои заработанные награды в вашу застейканную сумму, увеличивая потенциальный доход</string>
|
||||
|
|
@ -793,6 +803,7 @@
|
|||
<string name="staking_rewards">Вознаграждения</string>
|
||||
<string name="staking_stake_locked">Стейкинг закрыт</string>
|
||||
<string name="staking_stake_more">Застейкать еще</string>
|
||||
<string name="staking_staked_amount">Застейканная сумма</string>
|
||||
<string name="staking_summary_description_text">Вы стейкаете %1$s и будете получать награду %2$s</string>
|
||||
<string name="staking_tap_to_unlock">Нажмите для разблокировки</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Нажмите для разблокировки</string>
|
||||
|
|
@ -830,8 +841,9 @@
|
|||
<string name="swap_promo_text">Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке.</string>
|
||||
<string name="swap_promo_title">Новый провайдер обмена!</string>
|
||||
<string name="swapping_alert_cex_description">В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %1$s от биржи обратно на адрес пользователя \n\nПроскальзывание провайдера составляет до %2$s</string>
|
||||
<string name="swapping_alert_dex_description">В сумму включена комиссия провайдера сервиса.</string>
|
||||
<string name="swapping_alert_slippage_description">Проскальзывание провайдера составляет до %s.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">В сумму включена комиссия провайдера сервиса. \n\nПроскальзывание провайдера составляет до %s</string>
|
||||
<string name="swapping_alert_title">Информация</string>
|
||||
<string name="swapping_approve_information_text">Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен.</string>
|
||||
<string name="swapping_approve_information_title">Подтвердить</string>
|
||||
|
|
@ -925,7 +937,7 @@
|
|||
<string name="wallet_connect_error_with_framework_message">Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки.</string>
|
||||
<string name="wallet_connect_error_wrong_card_selected">Неверная карта или кольцо выбрана в приложении Tangem</string>
|
||||
<string name="wallet_connect_failed_to_build_tx">Не удалось создать транзакцию из данных Dapp. Код: %s</string>
|
||||
<string name="wallet_connect_generic_error_with_code">Произошла непредвиденная ошибка. Код ошибки: %d. Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки.</string>
|
||||
<string name="wallet_connect_generic_error_with_code">Произошла непредвиденная ошибка. Код ошибки: %d. Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать — обратитесь в службу поддержки.</string>
|
||||
<string name="wallet_connect_no_sessions_message">Нет открытых сессий WalletConnect</string>
|
||||
<string name="wallet_connect_no_sessions_title">Упс. Нет сессий.</string>
|
||||
<string name="wallet_connect_pairing_error">Не удалось создать пару WalletConnect: %1$s</string>
|
||||
|
|
@ -980,7 +992,7 @@
|
|||
<string name="warning_express_notification_invalid_reserve_amount_title">Сумма получения не может быть менее %s</string>
|
||||
<string name="warning_express_pair_unavailable_message">Это может произойти из-за того, что провайдер временно не предоставляет обмен выбранной вами пары. Пожалуйста, подождите некоторое время и попробуйте снова. (Код %s)</string>
|
||||
<string name="warning_express_pair_unavailable_title">Выбранная пара временно недоступна</string>
|
||||
<string name="warning_express_refresh_required_title">Cервис временно недоступен</string>
|
||||
<string name="warning_express_refresh_required_title">Сервис временно недоступен</string>
|
||||
<string name="warning_express_too_maximum_amount_title">Сумма для обмена должна быть не более %s</string>
|
||||
<string name="warning_express_too_minimal_amount_title">Сумма для обмена должна быть не менее %s</string>
|
||||
<string name="warning_express_wrong_amount_description">Пожалуйста, измените сумму для обмена</string>
|
||||
|
|
@ -1019,7 +1031,7 @@
|
|||
<string name="warning_solana_fee_message">Сеть Солана испытывает высокую нагрузку. Если Ваша транзакция не прошла в течение 2 минут, повторите её отправку.</string>
|
||||
<string name="warning_solana_fee_title">Оповещение сети Солана</string>
|
||||
<string name="warning_solana_rent_fee_message">Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Некоторые сети в настоящее время недоступны. Пожалуйста, повторите попытку позже.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Свайпните вниз для обновления или попробуйте позже.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Некоторые сети недоступны</string>
|
||||
<string name="warning_testnet_card_message">Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки.</string>
|
||||
<string name="warning_testnet_card_title">Только для целей тестирования</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">Токен не знайдено у вашому портфелі? Перевірте Ринки, щоб знайти та додати його для покупки</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Токен не знайдено у вашому портфелі? Перевірте Ринки, щоб знайти та додати його для продажу</string>
|
||||
<string name="action_buttons_swap_choose_token">Оберіть токен</string>
|
||||
<string name="action_buttons_swap_empty_search_message">Токен не знайдено у вашому портфелі? Перевірте ринки, щоб знайти та додати його для обміну</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">Немає доступних токенів для обміну з обраним токеном. Будь ласка, оберіть інший.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">Немає вільної пари</string>
|
||||
<string name="action_buttons_you_want_to_receive">Ви хочете отримати</string>
|
||||
<string name="action_buttons_you_want_to_swap">Ви хочете обміняти</string>
|
||||
<string name="add_custom_token_choose_network">Оберіть мережу</string>
|
||||
|
|
@ -523,7 +528,7 @@
|
|||
</plurals>
|
||||
<string name="onboarding_seed_generate_title">Ваша seed-фраза</string>
|
||||
<plurals name="onboarding_seed_generate_words_count">
|
||||
<item quantity="one">слово</item>
|
||||
<item quantity="one"></item>
|
||||
<item quantity="few">%d слова</item>
|
||||
<item quantity="many">%d слів</item>
|
||||
<item quantity="other">%d слів</item>
|
||||
|
|
@ -570,9 +575,16 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Відновлення коду доступу</string>
|
||||
<string name="onboarding_wallet_info_title_second">Ідентичні картки</string>
|
||||
<string name="onboarding_wallet_info_title_third">Код доступу</string>
|
||||
<string name="onramp_country_search">Пошук за країною</string>
|
||||
<string name="onramp_country_unavailable">Недоступно</string>
|
||||
<string name="onramp_currency_search">Пошук по валюті</string>
|
||||
<string name="onramp_pay_with">Оплата з</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Наші сервіси недоступні в цій країні</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">Змінити або підтвердити</string>
|
||||
<string name="onramp_residency_bottomsheet_title">Ваше місце проживання визначено як</string>
|
||||
<string name="onramp_settings_residence_description">Будь ласка, виберіть правильну країну, щоб забезпечити точні способи оплати та послуги.</string>
|
||||
<string name="onramp_settings_title">Параметри</string>
|
||||
<string name="onramp_via">Через</string>
|
||||
<string name="organize_tokens_group">Групами</string>
|
||||
<string name="organize_tokens_sort_by_balance">За балансом</string>
|
||||
<string name="organize_tokens_title">Сортування токенів</string>
|
||||
|
|
@ -719,9 +731,10 @@
|
|||
<string name="settings_wallet_name_title">Ім\'я</string>
|
||||
<string name="staking_amount_requirement_error">Сума для стейкінгу має бути не менше %s</string>
|
||||
<string name="staking_amount_tron_integer_error">Сума стейкінгу буде округлена до %1$s TRX відповідно до правил мережі.</string>
|
||||
<string name="staking_amount_tron_integer_error_unstaking">Сума зняття зі стейкінгу буде округлена до %1$s TRX через мережеві правила.</string>
|
||||
<string name="staking_claim_unstaked">Зняти кошти</string>
|
||||
<string name="staking_details_account_fee">Комісія за стейкінг-акаунт</string>
|
||||
<string name="staking_details_account_fee_info">Стейкінг-акаунт - це спеціальний рахунок, на якому зберігаються застейкані SOL токени. Він створюється, коли ви делегуєте свої токени валідатору для участі у перевірці транзакцій та отримання винагород. За створення стейкінг-акаунту стягується невелика комісія, яка повертається після завершення стейкінгу.</string>
|
||||
<string name="staking_details_account_fee_info">Стейкінг-акаунт — це спеціальний рахунок, на якому зберігаються застейкані SOL токени. Він створюється, коли ви делегуєте свої токени валідатору для участі у перевірці транзакцій та отримання винагород. За створення стейкінг-акаунту стягується невелика комісія, яка повертається після завершення стейкінгу.</string>
|
||||
<string name="staking_details_annual_percentage_rate">Процентна ставка</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Річний відсоток, який ви можете отримати, беручи участь у стейкінгу.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
|
|
@ -761,10 +774,13 @@
|
|||
<string name="staking_notification_earn_rewards_text_period_week">Стейкінг дає змогу вам отримувати %1$s. Винагорода буде зараховуватися кожен тиждень.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Стейкайте безпечно та почніть отримувати винагороди щотижня</string>
|
||||
<string name="staking_notification_earn_rewards_title">Отримуйте винагороду за стейкінг</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Ваш баланс стейкінгу, що залишився, буде занадто низьким, щоб вивести його зі стейкінгу. Вам потрібно буде застейкати більше, щоб досягти мінімальної суми для виводу.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Низький баланс стейкінгу</string>
|
||||
<string name="staking_notification_network_error_text">Стейкінг тимчасово недоступний через проблеми в мережі. Будь ласка, спробуйте пізніше.</string>
|
||||
<string name="staking_notification_new_validator_funds_transfer">Стейкінг в мережі %1$s з новим валідатором автоматично переведе всі раніше застейкані кошти до цього валідатора</string>
|
||||
<string name="staking_notification_restake_rewards_text">Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток.</string>
|
||||
<string name="staking_notification_restake_text">Рестейк дозволяє вам перемістити ваші кошти від одного валідатора до іншого без необхідності виводити кошти зі стейкінгу</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">Ви збираєтеся застейкати весь свій баланс. Ми рекомендуємо залишити невелику суму, щоб покрити комісію мережі за зняття коштів або отримання винагороди.</string>
|
||||
<string name="staking_notification_unlock_text">Розблокуйте свої кошти, щоб вивести їх зі стейкінгу. Розблокування займе %s.</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">Ваші кошти будуть доступні для використання після закінчення 21-денного періоду розблокування. Винагорода буде отримана разом з вашими незастейканими коштами.</string>
|
||||
<string name="staking_notification_unstake_text">Ваші кошти будуть доступні після %s періоду розблокування.</string>
|
||||
|
|
@ -793,6 +809,7 @@
|
|||
<string name="staking_rewards">Винагороди</string>
|
||||
<string name="staking_stake_locked">Стейкінг закрито</string>
|
||||
<string name="staking_stake_more">Застейкати більше</string>
|
||||
<string name="staking_staked_amount">Застейканий залишок</string>
|
||||
<string name="staking_summary_description_text">Ви стейкаєте %1$s і будете отримуватиме винагороду %2$s</string>
|
||||
<string name="staking_tap_to_unlock">Натисніть, щоб розблокувати</string>
|
||||
<string name="staking_tap_to_unlock_or_vote">Натисніть, щоб розблокувати або проголосувати</string>
|
||||
|
|
@ -803,7 +820,6 @@
|
|||
<string name="staking_unbonding">Розблокування</string>
|
||||
<string name="staking_unlocked_locked">Розблокувати</string>
|
||||
<string name="staking_unlocking">Розблокування</string>
|
||||
<string name="staking_unstake_amount_requirement_error">Сума для стейкінгу має бути не менше %s</string>
|
||||
<string name="staking_unstake_amount_validation_error">Сума перевищує баланс стейкінгу</string>
|
||||
<string name="staking_unstaked">Вивід зі стейкінгу</string>
|
||||
<string name="staking_unstaking">Зняти зі стейкінгу</string>
|
||||
|
|
@ -830,7 +846,9 @@
|
|||
<string name="swap_promo_text">Обмінюйте більше токенів за вигіднішим курсом прямо у своєму гаманці.</string>
|
||||
<string name="swap_promo_title">З\'явився новий провайдер обмінів!</string>
|
||||
<string name="swapping_alert_cex_description">Сума включає: \n• комісію постачальника послуг\n• комісію мережі за відправлення %s з біржі назад на адресу користувача.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">У суму входить:\n- комісія провайдера\n- мережева комісія за відправку %1$s з біржі назад на адресу користувача. \n\nПроскакування провайдера становить до %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Сума включає комісію постачальника послуг.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">Сума включає комісію провайдера послуг. \n\nПроскакування провайдера до %s</string>
|
||||
<string name="swapping_alert_title">Комісії</string>
|
||||
<string name="swapping_approve_information_text">Всі децентралізовані біржі вимагають схвалення, щоб запобігти доступу смарт-контрактів до вашого гаманця без вашого дозволу. За задумом смарт-контракти не можуть отримати доступ до ваших токенів без вашого схвалення. \"Розблоковуючи\" свої токени, ви дозволяєте смарт-контракту 1inch витрачати ваші активи. Майнери мережі отримують плату за газ (сплачену вами), щоб зафіксувати цю дію в блокчейні. Ви можете обміняти свій токен після того, як дасте дозвіл.</string>
|
||||
<string name="swapping_approve_information_title">Підтвердити</string>
|
||||
|
|
@ -881,6 +899,7 @@
|
|||
<string name="transaction_history_transaction_from_address">від: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">до: %s</string>
|
||||
<string name="transaction_history_transaction_validator">валідатор: %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">Мінімальна сума транзакції становить %1$s.</string>
|
||||
<string name="try_to_load_data_again_button_title">Спробуйте знову</string>
|
||||
<string name="twin_error_same_card">Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d</string>
|
||||
<string name="twin_error_wrong_twin">Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу</string>
|
||||
|
|
@ -1018,7 +1037,7 @@
|
|||
<string name="warning_solana_fee_message">Мережа Солана зазнає високого навантаження. Якщо транзакція не пройшла протягом 2 хвилин, повторіть транзакцію.</string>
|
||||
<string name="warning_solana_fee_title">Оповіщення мережі Солана</string>
|
||||
<string name="warning_solana_rent_fee_message">Мережа Solana стягує орендну плату у розмірі %1$s кожні 2 дні. Акаунти, які не можуть дозволити собі орендну плату, видаляються з мережі. Поповніть свій рахунок на суму понад %2$s, щоб не платити орендну плату.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Деякі мережі наразі недоступні. Будь ласка, спробуйте пізніше.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Проведіть пальцем вниз для оновлення або спробуйте пізніше.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Деякі мережі недоступні</string>
|
||||
<string name="warning_testnet_card_message">Це картка Testnet. Вона не може обробляти транзакції і повинна використовуватися лише для тестування та розробки.</string>
|
||||
<string name="warning_testnet_card_title">Лише для цілей тестування</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_buttons_buy_empty_search_message">Token not found in your portfolio? Check the Markets to find and add it for purchase</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Token not found in your portfolio? Check the Markets to find and add it for sell</string>
|
||||
<string name="action_buttons_swap_choose_token">Choose the Token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">Token not found in your portfolio? Check the Markets to find and add it for swap</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">There are no available tokens to swap with the selected token. Please choose another one.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">No available pair</string>
|
||||
<string name="action_buttons_you_want_to_receive">You want to Receive</string>
|
||||
<string name="action_buttons_you_want_to_swap">You want to Swap</string>
|
||||
<string name="add_custom_token_choose_network">Choose network</string>
|
||||
|
|
@ -448,6 +453,7 @@
|
|||
<string name="markets_token_details_price_performance">Price performance</string>
|
||||
<string name="markets_token_details_repository">Repository</string>
|
||||
<string name="markets_token_details_security_score">Security score</string>
|
||||
<string name="markets_token_details_security_score_description">Security score of a token is a metric that assesses the security level of a blockchain or token based on various factors and is compiled from the sources listed below.</string>
|
||||
<string name="markets_token_details_social">Social</string>
|
||||
<string name="markets_token_details_total_supply">Total supply</string>
|
||||
<string name="markets_token_details_total_supply_description">The maximum number of coins or tokens that can ever exist for a particular cryptocurrency</string>
|
||||
|
|
@ -557,7 +563,12 @@
|
|||
<string name="onramp_country_search">Search by country</string>
|
||||
<string name="onramp_country_unavailable">Unavailable</string>
|
||||
<string name="onramp_currency_search">Search by currency</string>
|
||||
<string name="onramp_max_amount_restriction">The purchase amount should be no more than %s</string>
|
||||
<string name="onramp_min_amount_restriction">The amount to buy must be at least %s</string>
|
||||
<string name="onramp_no_available_providers">No available providers for this currency</string>
|
||||
<string name="onramp_pay_with">Pay with</string>
|
||||
<string name="onramp_redirecting_to_provider_subtitle">You will be able to complete your transaction on the third-party provider, %s</string>
|
||||
<string name="onramp_redirecting_to_provider_title">Redirecting to %s...</string>
|
||||
<string name="onramp_residency_bottomsheet_country_not_supported">Our services are not available in this country</string>
|
||||
<string name="onramp_residency_bottomsheet_country_subtitle">Change or confirm it</string>
|
||||
<string name="onramp_residency_bottomsheet_title">Your residence has been identified as</string>
|
||||
|
|
@ -823,8 +834,9 @@
|
|||
<string name="swap_promo_text">Exchange more tokens at better rates directly in your wallet.</string>
|
||||
<string name="swap_promo_title">New Swap Provider Available!</string>
|
||||
<string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">The amount includes:\n• service provider\'s fee\n• network fee for sending %1$s from the exchange back to the user\'s address. \n\nProvider slippage is up to %2$s</string>
|
||||
<string name="swapping_alert_dex_description">The amount includes the service provider\'s fee.</string>
|
||||
<string name="swapping_alert_slippage_description">Provider slippage is up to %s.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">The amount includes the service provider\'s fee. \n\nProvider slippage is up to %s</string>
|
||||
<string name="swapping_alert_title">Information</string>
|
||||
<string name="swapping_approve_information_text">All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval.</string>
|
||||
<string name="swapping_approve_information_title">Approve</string>
|
||||
|
|
@ -875,6 +887,7 @@
|
|||
<string name="transaction_history_transaction_from_address">from: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">to: %s</string>
|
||||
<string name="transaction_history_transaction_validator">validator: %s</string>
|
||||
<string name="transfer_min_amount_error">Minimum %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">The minimum transaction amount is %1$s.</string>
|
||||
<string name="try_to_load_data_again_button_title">Try again</string>
|
||||
<string name="twin_error_same_card">You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d</string>
|
||||
|
|
@ -1011,7 +1024,7 @@
|
|||
<string name="warning_solana_fee_message">The Solana network is congested. If your transaction is not processed within 2 minutes, please repeat the transaction.</string>
|
||||
<string name="warning_solana_fee_title">Solana Network Alert</string>
|
||||
<string name="warning_solana_rent_fee_message">Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Some networks currently are unreachable. Please try again later.</string>
|
||||
<string name="warning_some_networks_unreachable_message">Swipe down to refresh or try again later.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Some networks are unreachable</string>
|
||||
<string name="warning_testnet_card_message">This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.</string>
|
||||
<string name="warning_testnet_card_title">For testing purposes only</string>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.CRYPTO_FEE
|
|||
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.CURRENCY_SPACE
|
||||
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.FORMAT_THRESHOLD
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.utils.StringsSigns.NON_BREAKING_SPACE
|
||||
import com.tangem.utils.extensions.isNotWhitespace
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
|
@ -16,6 +17,7 @@ open class BigDecimalCryptoFormat(
|
|||
val symbol: String,
|
||||
val decimals: Int,
|
||||
val locale: Locale = Locale.getDefault(),
|
||||
val ignoreSymbolPosition: Boolean = false,
|
||||
) : BigDecimalFormat {
|
||||
|
||||
override fun invoke(value: BigDecimal): String = defaultAmount()(value)
|
||||
|
|
@ -48,11 +50,13 @@ fun BigDecimalFormatScope.crypto(
|
|||
|
||||
fun BigDecimalFormatScope.crypto(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
ignoreSymbolPosition: Boolean = false,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): BigDecimalCryptoFormat {
|
||||
return BigDecimalCryptoFormat(
|
||||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
ignoreSymbolPosition = ignoreSymbolPosition,
|
||||
locale = locale,
|
||||
)
|
||||
}
|
||||
|
|
@ -60,19 +64,28 @@ fun BigDecimalFormatScope.crypto(
|
|||
// == Formatters ==
|
||||
|
||||
fun BigDecimalCryptoFormat.defaultAmount() = BigDecimalFormat { value ->
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = usdCurrency
|
||||
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
if (ignoreSymbolPosition) {
|
||||
val formatter = NumberFormat.getInstance(locale).apply {
|
||||
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
formatter.format(value) + NON_BREAKING_SPACE + symbol
|
||||
} else {
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = usdCurrency
|
||||
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
formatter.format(value)
|
||||
.replaceFiatSymbolWithCrypto(
|
||||
fiatCurrencySymbol = usdCurrency.getSymbol(locale),
|
||||
cryptoCurrencySymbol = symbol,
|
||||
)
|
||||
}
|
||||
|
||||
formatter.format(value)
|
||||
.replaceFiatSymbolWithCrypto(
|
||||
fiatCurrencySymbol = usdCurrency.getSymbol(locale),
|
||||
cryptoCurrencySymbol = symbol,
|
||||
)
|
||||
}
|
||||
|
||||
fun BigDecimalCryptoFormat.shorted() = BigDecimalFormat { value ->
|
||||
|
|
|
|||
|
|
@ -12,4 +12,7 @@ fun BigDecimal.isZero(): Boolean = this.compareTo(BigDecimal.ZERO) == 0
|
|||
fun BigDecimal.isPositive(): Boolean = this.signum() == 1
|
||||
|
||||
/** Removes trailing zeros and returns plain [String] */
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
||||
/** Compares two [BigDecimal] numbers */
|
||||
infix fun BigDecimal.isEqualTo(other: BigDecimal): Boolean = this.compareTo(other) == 0
|
||||
|
|
@ -55,7 +55,9 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
internal class DefaultStakingRepository(
|
||||
|
|
@ -131,7 +133,7 @@ internal class DefaultStakingRepository(
|
|||
val rawCurrencyId = cryptoCurrencyId.rawCurrencyId ?: error("Staking custom tokens is not available")
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = getEnabledYields(),
|
||||
yields = getEnabledYieldsSync(),
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = symbol,
|
||||
)
|
||||
|
|
@ -186,7 +188,7 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getStakingAvailability(
|
||||
override suspend fun getStakingAvailability(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): StakingAvailability {
|
||||
|
|
@ -197,7 +199,7 @@ internal class DefaultStakingRepository(
|
|||
val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not()
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = getEnabledYields(),
|
||||
yields = getEnabledYieldsSync(),
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
|
|
@ -406,12 +408,22 @@ internal class DefaultStakingRepository(
|
|||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val yields = getEnabledYields().ifEmpty {
|
||||
val yieldDTOs = withTimeoutOrNull(YIELDS_WATITING_TIMEOUT) {
|
||||
runCatching { stakingYieldsStore.get().firstOrNull() }.getOrNull()
|
||||
}
|
||||
|
||||
if (yieldDTOs == null) {
|
||||
Timber.i("No enabled yields for $userWalletId")
|
||||
stakingBalanceStore.store(userWalletId, emptySet())
|
||||
|
||||
return@invokeOnExpire
|
||||
}
|
||||
|
||||
val yields = yieldConverter.convertListIgnoreErrors(
|
||||
input = yieldDTOs,
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
|
||||
val availableCurrencies = cryptoCurrencies
|
||||
.mapNotNull { currency ->
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
|
|
@ -428,7 +440,6 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
.map { getBalanceRequestData(it.first.value, it.second) }
|
||||
.ifEmpty {
|
||||
Timber.i("No yield balances available for $userWalletId")
|
||||
stakingBalanceStore.store(userWalletId, emptySet())
|
||||
|
||||
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
|
||||
|
|
@ -564,10 +575,10 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getEnabledYields(): List<Yield> {
|
||||
private suspend fun getEnabledYieldsSync(): List<Yield> {
|
||||
return yieldConverter.convertListIgnoreErrors(
|
||||
input = stakingYieldsStore.get(),
|
||||
onError = { Timber.e("Error converting enabled yields list: $it") },
|
||||
input = stakingYieldsStore.getSync(),
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -614,6 +625,8 @@ internal class DefaultStakingRepository(
|
|||
|
||||
const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908"
|
||||
|
||||
val YIELDS_WATITING_TIMEOUT = 15.seconds
|
||||
|
||||
val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79")
|
||||
|
||||
// uncomment items as implementation is ready
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ internal class YieldBalanceConverter : Converter<YieldBalanceWrapperDTO, YieldBa
|
|||
validatorAddress = item.validatorAddress ?: item.validatorAddresses?.get(0),
|
||||
date = item.date?.toDateTime(),
|
||||
pendingActions = pendingActionConverter
|
||||
.convertList(item.pendingActions),
|
||||
.convertList(item.pendingActions)
|
||||
.sortedBy { it.passthrough },
|
||||
isPending = false,
|
||||
)
|
||||
},
|
||||
}
|
||||
.sortedWith(compareBy({ it.type }, { it.amount })),
|
||||
integrationId = value.integrationId,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -118,16 +118,16 @@ class YieldConverter(
|
|||
val address = validatorDTO.address.asMandatory("address")
|
||||
return Yield.Validator(
|
||||
address = address,
|
||||
status = convertValidatorStatus(validatorDTO.status),
|
||||
name = validatorDTO.name,
|
||||
status = convertValidatorStatus(validatorDTO.status.asMandatory("status")),
|
||||
name = validatorDTO.name.asMandatory("name"),
|
||||
image = validatorDTO.image,
|
||||
website = validatorDTO.website,
|
||||
apr = validatorDTO.apr,
|
||||
commission = validatorDTO.commission,
|
||||
stakedBalance = validatorDTO.stakedBalance,
|
||||
votingPower = validatorDTO.votingPower,
|
||||
preferred = validatorDTO.preferred,
|
||||
isStrategicPartner = isStrategicPartner(validatorDTO.address, validatorDTO.name),
|
||||
preferred = validatorDTO.preferred.asMandatory("preferred"),
|
||||
isStrategicPartner = isStrategicPartner(validatorDTO.address, validatorDTO.name.asMandatory("name")),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,8 +83,14 @@ internal object TokensDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCurrencyChecksRepository(walletManagersFacade: WalletManagersFacade): CurrencyChecksRepository {
|
||||
return DefaultCurrencyChecksRepository(walletManagersFacade = walletManagersFacade)
|
||||
fun provideCurrencyChecksRepository(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
coroutineDispatcherProvider: CoroutineDispatcherProvider,
|
||||
): CurrencyChecksRepository {
|
||||
return DefaultCurrencyChecksRepository(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
coroutineDispatchers = coroutineDispatcherProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.tokens.repository
|
|||
|
||||
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
|
||||
import com.tangem.blockchain.common.FeeResourceAmountProvider
|
||||
import com.tangem.blockchain.common.MinimumSendAmountProvider
|
||||
import com.tangem.blockchain.common.ReserveAmountProvider
|
||||
import com.tangem.blockchain.common.UtxoAmountLimitProvider
|
||||
import com.tangem.data.tokens.converters.UtxoConverter
|
||||
|
|
@ -11,10 +12,13 @@ import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit
|
|||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class DefaultCurrencyChecksRepository(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val coroutineDispatchers: CoroutineDispatcherProvider,
|
||||
) : CurrencyChecksRepository {
|
||||
override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? {
|
||||
val manager = walletManagersFacade.getOrCreateWalletManager(
|
||||
|
|
@ -43,6 +47,17 @@ internal class DefaultCurrencyChecksRepository(
|
|||
return if (manager is ReserveAmountProvider) manager.getReserveAmount() else null
|
||||
}
|
||||
|
||||
override suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
val manager = walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
if (manager is MinimumSendAmountProvider) manager.getMinimumSendAmount() else null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? {
|
||||
val manager = walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
|
|
|
|||
|
|
@ -141,5 +141,65 @@
|
|||
"info":"swapTokensForExactETC",
|
||||
"source":"https://etc.blockscout.com/tx/0xd79e03ca6b71529c94d576ff78c55b4371e2ef3ff8f9a47007995e5b9e77f879",
|
||||
"name":"swap"
|
||||
},
|
||||
"0x6ab15071": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0x490d2f31faa3f2fdb1a9505888761d89aba1dd3dcfeb65f08651d094c1ee5021",
|
||||
"name": "buyVoucher"
|
||||
},
|
||||
"0xe4457a8a": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0xb7b67ea0261fe2067e285eb942be6883ef34b3d313160f3ce122cad3e91f95f7",
|
||||
"name": "buyVoucherPOL"
|
||||
},
|
||||
"0xc83ec04d": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0x0f7fa7154be5ae9bbe229963e3c0d3eea16b74311c50c4a35f3fcffc9519baed",
|
||||
"name": "sellVoucher_new"
|
||||
},
|
||||
"0xe570b78b": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0xde1dda9ff3aed8b1d5feff53f72032cbd106a57c1ae2e7fa80016349e2e68341",
|
||||
"name": "sellVoucher_newPOL"
|
||||
},
|
||||
"0xe97fddc2": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0x0478bc1229722c049eed737ef5a56baee6d7bde7ae21afad7a10e029700b378c",
|
||||
"name": "unstakeClaimTokens_new"
|
||||
},
|
||||
"0x8759c234": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0x79075778b470358bef4cfc25a74a43e7ae60f1bc292e0bca3ebc163fee8bf0c7",
|
||||
"name": "unstakeClaimTokens_newPOL"
|
||||
},
|
||||
"0xc7b8981c": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0xc2df9b41f7b3780a2dd06f0dfff72ecd5d3a5bde3f1d3f0eb926e14ff975e957",
|
||||
"name": "withdrawRewards"
|
||||
},
|
||||
"0xe0db556b": {
|
||||
"info": "stakePOL",
|
||||
"source": "https://etherscan.io/tx/0x96ee64ad31a045982a4776336d852af3c25dc39b6d01bea987aefd460b816773",
|
||||
"name": "withdrawRewardsPOL"
|
||||
},
|
||||
"0x982ef0a7": {
|
||||
"info": "stakeBSC",
|
||||
"source": "https://bscscan.com/tx/0xd5ab62ee525f3dc2821de44c31e278f2b49a0202f4e0279e6d0fced05a034595",
|
||||
"name": "delegate"
|
||||
},
|
||||
"0x4d99dd16": {
|
||||
"info": "stakeBSC",
|
||||
"source": "https://bscscan.com/tx/0x866ba857e96051d7cea271723fccde4113c9909dccb534202a2a87ad4c56cb67",
|
||||
"name": "undelegate"
|
||||
},
|
||||
"0x59491871": {
|
||||
"info": "stakeBSC",
|
||||
"source": "https://bscscan.com/tx/0x69615650be1e8a6d94258483f4e880fc6b4d4d9ae53d967c82d847691d91dd71",
|
||||
"name": "redelegate"
|
||||
},
|
||||
"0xaad3ec96": {
|
||||
"info": "stakeBSC",
|
||||
"source": "https://bscscan.com/tx/0xfcbeac6e0a4ba5768d97d14b5115d08dda92defa8a8deee8bba34e2f0655c52b",
|
||||
"name": "claim"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,19 +21,19 @@ internal class SdkTransactionTypeConverter(
|
|||
TxHistoryItem.TransactionType.Transfer
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.FreezeBalanceV2Contract -> {
|
||||
TxHistoryItem.TransactionType.TronStakingTransactionType.Stake
|
||||
TxHistoryItem.TransactionType.Staking.Stake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.UnfreezeBalanceV2Contract -> {
|
||||
TxHistoryItem.TransactionType.TronStakingTransactionType.Unstake
|
||||
TxHistoryItem.TransactionType.Staking.Unstake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.VoteWitnessContract -> {
|
||||
TxHistoryItem.TransactionType.TronStakingTransactionType.Vote(value.validatorAddress)
|
||||
TxHistoryItem.TransactionType.Staking.Vote(value.validatorAddress)
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawBalanceContract -> {
|
||||
TxHistoryItem.TransactionType.TronStakingTransactionType.ClaimRewards
|
||||
TxHistoryItem.TransactionType.Staking.ClaimRewards
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawExpireUnfreezeContract -> {
|
||||
TxHistoryItem.TransactionType.TronStakingTransactionType.Withdraw
|
||||
TxHistoryItem.TransactionType.Staking.Withdraw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,22 @@ internal class SdkTransactionTypeConverter(
|
|||
"transfer" -> TxHistoryItem.TransactionType.Transfer
|
||||
"approve" -> TxHistoryItem.TransactionType.Approve
|
||||
"swap" -> TxHistoryItem.TransactionType.Swap
|
||||
"buyVoucher",
|
||||
"buyVoucherPOL",
|
||||
"delegate",
|
||||
-> TxHistoryItem.TransactionType.Staking.Stake
|
||||
"sellVoucher_new",
|
||||
"sellVoucher_newPOL",
|
||||
"undelegate",
|
||||
-> TxHistoryItem.TransactionType.Staking.Unstake
|
||||
"unstakeClaimTokens_new",
|
||||
"unstakeClaimTokens_newPOL",
|
||||
"claim",
|
||||
-> TxHistoryItem.TransactionType.Staking.Withdraw
|
||||
"withdrawRewards",
|
||||
"withdrawRewardsPOL",
|
||||
-> TxHistoryItem.TransactionType.Staking.ClaimRewards
|
||||
"redelegate" -> TxHistoryItem.TransactionType.Staking.Restake
|
||||
null -> TxHistoryItem.TransactionType.UnknownOperation
|
||||
else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ dependencies {
|
|||
api(projects.domain.staking.models)
|
||||
api(projects.domain.core)
|
||||
api(projects.core.analytics)
|
||||
api(projects.core.utils)
|
||||
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.jodatime)
|
||||
|
|
|
|||
|
|
@ -25,9 +25,10 @@ enum class StakingActionType {
|
|||
UNSTAKE -> "Unstake"
|
||||
CLAIM_REWARDS -> "Claim Rewards"
|
||||
RESTAKE_REWARDS -> "Restake Rewards"
|
||||
WITHDRAW -> "Withdraw"
|
||||
CLAIM_UNSTAKED,
|
||||
WITHDRAW,
|
||||
-> "Withdraw"
|
||||
RESTAKE -> "Restake"
|
||||
CLAIM_UNSTAKED -> "Claim Unstaked"
|
||||
UNLOCK_LOCKED -> "Unlock Locked"
|
||||
STAKE_LOCKED -> "Stake Locked"
|
||||
VOTE -> "Vote"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class GetStakingAvailabilityUseCase(
|
|||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Either<StakingError, StakingAvailability> {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.domain.staking.model.stakekit.StakingError
|
|||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.utils.extensions.isEqualTo
|
||||
import java.math.BigDecimal
|
||||
import java.util.UUID
|
||||
|
||||
class InvalidatePendingTransactionsUseCase(
|
||||
|
|
@ -36,8 +38,12 @@ class InvalidatePendingTransactionsUseCase(
|
|||
|
||||
processingActions.forEach { action ->
|
||||
when (action.type) {
|
||||
StakingActionType.STAKE, StakingActionType.VOTE, StakingActionType.VOTE_LOCKED -> {
|
||||
processEnterAction(balances, action)
|
||||
StakingActionType.STAKE, StakingActionType.VOTE -> {
|
||||
addStubStakedPendingTransaction(balances, action)
|
||||
}
|
||||
StakingActionType.VOTE_LOCKED -> {
|
||||
addStubStakedPendingTransaction(balances, action)
|
||||
removeLockedBalance(balances, action)
|
||||
}
|
||||
StakingActionType.WITHDRAW -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.UNSTAKED)
|
||||
|
|
@ -45,9 +51,15 @@ class InvalidatePendingTransactionsUseCase(
|
|||
StakingActionType.UNLOCK_LOCKED -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.LOCKED)
|
||||
}
|
||||
StakingActionType.UNSTAKE -> {
|
||||
StakingActionType.RESTAKE -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.STAKED)
|
||||
}
|
||||
StakingActionType.UNSTAKE -> {
|
||||
val isFullUnstake = modifyBalancesByStatus(balances, action, BalanceType.STAKED)
|
||||
if (!isFullUnstake) {
|
||||
processPartialUnstake(balances, action)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// intentionally do nothing
|
||||
}
|
||||
|
|
@ -57,7 +69,15 @@ class InvalidatePendingTransactionsUseCase(
|
|||
return balances
|
||||
}
|
||||
|
||||
private fun processEnterAction(balances: MutableList<BalanceItem>, action: StakingAction) {
|
||||
private fun removeLockedBalance(balances: MutableList<BalanceItem>, action: StakingAction) {
|
||||
val index = findBalanceIndex(balances, action, BalanceType.LOCKED)
|
||||
|
||||
if (index != -1) {
|
||||
balances.removeAt(index)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addStubStakedPendingTransaction(balances: MutableList<BalanceItem>, action: StakingAction) {
|
||||
balances.add(
|
||||
BalanceItem(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
|
|
@ -73,13 +93,51 @@ class InvalidatePendingTransactionsUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private fun modifyBalancesByStatus(balances: MutableList<BalanceItem>, action: StakingAction, type: BalanceType) {
|
||||
val index = balances.indexOfFirst {
|
||||
!it.isPending && it.amount == action.amount && it.type == type
|
||||
}
|
||||
private fun modifyBalancesByStatus(
|
||||
balances: MutableList<BalanceItem>,
|
||||
action: StakingAction,
|
||||
type: BalanceType,
|
||||
): Boolean {
|
||||
val index = findBalanceIndex(balances, action, type)
|
||||
|
||||
if (index != -1) {
|
||||
balances[index] = balances[index].copy(isPending = true)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findBalanceIndex(balances: MutableList<BalanceItem>, action: StakingAction, type: BalanceType): Int {
|
||||
return balances.indexOfFirst {
|
||||
!it.isPending && it.amount isEqualTo action.amount && it.type == type
|
||||
}
|
||||
}
|
||||
|
||||
private fun processPartialUnstake(balances: MutableList<BalanceItem>, action: StakingAction) {
|
||||
val (index, pendingActionAmount) = findPartialUnstake(balances, action)
|
||||
|
||||
if (index != -1) {
|
||||
val amount = balances[index].amount
|
||||
balances[index] = balances[index].copy(
|
||||
amount = amount - pendingActionAmount,
|
||||
) // remnants of real one
|
||||
|
||||
balances.add(
|
||||
balances[index].copy(
|
||||
amount = pendingActionAmount,
|
||||
isPending = true,
|
||||
),
|
||||
) // pending with amount from action
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPartialUnstake(balances: MutableList<BalanceItem>, action: StakingAction): Pair<Int, BigDecimal> {
|
||||
val index = balances.indexOfFirst {
|
||||
!it.isPending && action.amount < it.amount &&
|
||||
it.type == BalanceType.STAKED &&
|
||||
it.validatorAddress == action.validatorAddress
|
||||
}
|
||||
return index to action.amount
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ interface StakingRepository {
|
|||
|
||||
suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield
|
||||
|
||||
fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
|
||||
suspend fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
|
||||
|
||||
suspend fun getActions(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import java.math.BigDecimal
|
|||
data class CryptoCurrencyCheck(
|
||||
val dustValue: BigDecimal?,
|
||||
val reserveAmount: BigDecimal?,
|
||||
val minimumSendAmount: BigDecimal?,
|
||||
val existentialDeposit: BigDecimal?,
|
||||
val utxoAmountLimit: UtxoAmountLimit?,
|
||||
val isAccountFunded: Boolean,
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
return networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun isStakingAvailable(userWallet: UserWallet, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
private suspend fun isStakingAvailable(userWallet: UserWallet, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
return stakingRepository.getStakingAvailability(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class GetCurrencyCheckUseCase(
|
|||
val network = currencyStatus.currency.network
|
||||
val dustValue = currencyChecksRepository.getDustValue(userWalletId, network)
|
||||
val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network)
|
||||
val minimumSendAmount = currencyChecksRepository.getMinimumSendAmount(userWalletId, network)
|
||||
val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network)
|
||||
val isAccountFunded = recipientAddress?.let {
|
||||
currencyChecksRepository.checkIfAccountFunded(
|
||||
|
|
@ -46,6 +47,7 @@ class GetCurrencyCheckUseCase(
|
|||
CryptoCurrencyCheck(
|
||||
dustValue = dustValue,
|
||||
reserveAmount = reserveAmount,
|
||||
minimumSendAmount = minimumSendAmount,
|
||||
existentialDeposit = existentialDeposit,
|
||||
utxoAmountLimit = utxoAmountLimit,
|
||||
isAccountFunded = isAccountFunded,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import java.math.BigDecimal
|
||||
|
||||
class GetMinimumTransactionAmountSyncUseCase(
|
||||
private val currencyChecksRepository: CurrencyChecksRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): Either<Throwable, BigDecimal?> = either {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
currencyChecksRepository.getMinimumSendAmount(userWalletId, cryptoCurrency.network)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,9 @@ interface CurrencyChecksRepository {
|
|||
/** Returns reserve amount which is required to create an account */
|
||||
suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal?
|
||||
|
||||
/** Returns minimum send transaction amount */
|
||||
suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal?
|
||||
|
||||
/** Returns a fee resource amount available and max for paying fees in several blockchains */
|
||||
suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount?
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class MockStakingRepository : StakingRepository {
|
|||
isAvailable = false,
|
||||
)
|
||||
|
||||
override fun getStakingAvailability(
|
||||
override suspend fun getStakingAvailability(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): StakingAvailability = StakingAvailability.Unavailable
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ import com.tangem.domain.transaction.error.GetFeeError
|
|||
import com.tangem.domain.transaction.error.mapToFeeError
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
@ -30,28 +28,25 @@ class EstimateFeeUseCase(
|
|||
amount: BigDecimal,
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Flow<Either<GetFeeError, TransactionFee>> {
|
||||
return flow {
|
||||
val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount)
|
||||
val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
demoTransactionSender(userWallet, cryptoCurrency).estimateFee(
|
||||
amount = amountData,
|
||||
destination = "",
|
||||
)
|
||||
} else {
|
||||
walletManagersFacade.estimateFee(
|
||||
amount = amountData,
|
||||
userWalletId = userWallet.walletId,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
}
|
||||
): Either<GetFeeError, TransactionFee> {
|
||||
val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount)
|
||||
val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
demoTransactionSender(userWallet, cryptoCurrency).estimateFee(
|
||||
amount = amountData,
|
||||
destination = "",
|
||||
)
|
||||
} else {
|
||||
walletManagersFacade.estimateFee(
|
||||
amount = amountData,
|
||||
userWalletId = userWallet.walletId,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
}
|
||||
|
||||
val maybeFee = when (result) {
|
||||
is Result.Success -> result.data.right()
|
||||
is Result.Failure -> result.mapToFeeError().left()
|
||||
null -> GetFeeError.UnknownError.left()
|
||||
}
|
||||
emit(maybeFee)
|
||||
return when (result) {
|
||||
is Result.Success -> result.data.right()
|
||||
is Result.Failure -> result.mapToFeeError().left()
|
||||
null -> GetFeeError.UnknownError.left()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,12 +40,13 @@ data class TxHistoryItem(
|
|||
data object UnknownOperation : TransactionType
|
||||
data class Operation(val name: String) : TransactionType
|
||||
|
||||
sealed interface TronStakingTransactionType : TransactionType {
|
||||
data class Vote(val validatorAddress: String) : TronStakingTransactionType
|
||||
data object ClaimRewards : TronStakingTransactionType
|
||||
data object Stake : TronStakingTransactionType
|
||||
data object Unstake : TronStakingTransactionType
|
||||
data object Withdraw : TronStakingTransactionType
|
||||
sealed interface Staking : TransactionType {
|
||||
data class Vote(val validatorAddress: String) : Staking
|
||||
data object ClaimRewards : Staking
|
||||
data object Stake : Staking
|
||||
data object Unstake : Staking
|
||||
data object Withdraw : Staking
|
||||
data object Restake : Staking
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ internal class SendStateFactory(
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
maxEnterAmountProvider = Provider { maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()) },
|
||||
maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()),
|
||||
)
|
||||
}
|
||||
private val recipientStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
|
|
@ -16,6 +17,7 @@ internal class AmountStateFactory(
|
|||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
|
||||
) {
|
||||
|
||||
private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -23,6 +25,7 @@ internal class AmountStateFactory(
|
|||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
|
||||
)
|
||||
}
|
||||
private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -30,6 +33,7 @@ internal class AmountStateFactory(
|
|||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +55,7 @@ internal class AmountStateFactory(
|
|||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
|
||||
)
|
||||
}
|
||||
private val amountReduceToConverter by lazy {
|
||||
|
|
@ -58,6 +63,7 @@ internal class AmountStateFactory(
|
|||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
|
|
@ -11,6 +12,7 @@ internal class SendAmountReduceByConverter(
|
|||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
|
||||
) : Converter<AmountReduceByTransformer.ReduceByData, SendUiState> {
|
||||
|
||||
override fun convert(value: AmountReduceByTransformer.ReduceByData): SendUiState {
|
||||
|
|
@ -23,7 +25,11 @@ internal class SendAmountReduceByConverter(
|
|||
sendState = state.sendState?.copy(
|
||||
reduceAmountBy = value.reduceAmountBy,
|
||||
),
|
||||
amountState = AmountReduceByTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState),
|
||||
amountState = AmountReduceByTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
|
||||
minimumTransactionAmount = minimumTransactionAmountProvider(),
|
||||
value = value,
|
||||
).transform(amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
|
|
@ -12,6 +13,7 @@ internal class SendAmountReduceToConverter(
|
|||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
|
||||
) : Converter<BigDecimal, SendUiState> {
|
||||
|
||||
override fun convert(value: BigDecimal): SendUiState {
|
||||
|
|
@ -21,7 +23,11 @@ internal class SendAmountReduceToConverter(
|
|||
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = AmountReduceToTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState),
|
||||
amountState = AmountReduceToTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
|
||||
minimumTransactionAmount = minimumTransactionAmountProvider(),
|
||||
value = value,
|
||||
).transform(amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.common.ui.notifications.NotificationsFactory.addExceedsBalance
|
|||
import com.tangem.common.ui.notifications.NotificationsFactory.addExistentialWarningNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addFeeCoverageNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addMinimumAmountErrorNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addReserveAmountErrorNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addTransactionLimitErrorNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addValidateTransactionNotifications
|
||||
|
|
@ -192,6 +193,11 @@ internal class SendNotificationFactory(
|
|||
cryptoCurrency = currency,
|
||||
isAccountFunded = currencyCheck.isAccountFunded,
|
||||
)
|
||||
addMinimumAmountErrorNotification(
|
||||
minimumSendAmount = currencyCheck.minimumSendAmount,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<NotificationUM>.addWarningNotifications(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
|
|
@ -12,21 +13,29 @@ internal class SendAmountFieldChangeConverter(
|
|||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
|
||||
) : Converter<String, SendUiState> {
|
||||
|
||||
private val maxEnterAmountConverter = MaxEnterAmountConverter()
|
||||
|
||||
override fun convert(value: String): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = state.getAmountState(isEditState)
|
||||
|
||||
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider())
|
||||
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus)
|
||||
val minimumTransactionAmount = minimumTransactionAmountProvider()
|
||||
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
sendState = state.sendState?.copy(reduceAmountBy = null),
|
||||
amountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(amountState),
|
||||
amountState = AmountFieldChangeTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxEnterAmount,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = value,
|
||||
).transform(amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
|
|
@ -13,6 +14,7 @@ internal class SendAmountFieldMaxAmountConverter(
|
|||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
|
||||
) : Converter<Unit, SendUiState> {
|
||||
|
||||
private val maxEnterAmountConverter = MaxEnterAmountConverter()
|
||||
|
|
@ -27,11 +29,16 @@ internal class SendAmountFieldMaxAmountConverter(
|
|||
if (decimalCryptoValue.isNullOrZero()) return state
|
||||
|
||||
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus)
|
||||
val minimumTransactionAmount = minimumTransactionAmountProvider()
|
||||
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
sendState = state.sendState?.copy(reduceAmountBy = null),
|
||||
amountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount).transform(amountState),
|
||||
amountState = AmountFieldSetMaxAmountTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = maxEnterAmount,
|
||||
minAmount = minimumTransactionAmount,
|
||||
).transform(amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
|
|
@ -59,6 +60,7 @@ import com.tangem.features.send.impl.presentation.state.recipient.RecipientSendF
|
|||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
|
|
@ -78,6 +80,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
|
||||
private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase,
|
||||
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
|
|
@ -153,6 +156,7 @@ internal class SendViewModel @Inject constructor(
|
|||
stateRouterProvider = Provider { stateRouter },
|
||||
currentStateProvider = Provider { uiState.value },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
minimumTransactionAmountProvider = Provider { minimumTransactionAmount },
|
||||
)
|
||||
|
||||
private val feeStateFactory = FeeStateFactory(
|
||||
|
|
@ -212,6 +216,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private var isTapHelpPreviewEnabled: Boolean = false
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var minimumTransactionAmount: EnterAmountBoundary? = null
|
||||
|
||||
private var balanceJobHolder = JobHolder()
|
||||
private var balanceHidingJobHolder = JobHolder()
|
||||
|
|
@ -295,6 +300,7 @@ internal class SendViewModel @Inject constructor(
|
|||
onDataLoaded(
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = getFeeCurrencyStatusSync(cryptoCurrencyStatus, isMultiCurrency),
|
||||
minTransactionAmount = getMinimumTransactionAmount(cryptoCurrencyStatus),
|
||||
)
|
||||
},
|
||||
ifLeft = { showErrorAlert() },
|
||||
|
|
@ -336,6 +342,18 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getMinimumTransactionAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): EnterAmountBoundary? {
|
||||
return getMinimumTransactionAmountSyncUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
).getOrNull()?.let {
|
||||
EnterAmountBoundary(
|
||||
amount = it,
|
||||
fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
|
|
@ -348,9 +366,14 @@ internal class SendViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus?) {
|
||||
private fun onDataLoaded(
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
minTransactionAmount: EnterAmountBoundary?,
|
||||
) {
|
||||
cryptoCurrencyStatus = currencyStatus
|
||||
feeCryptoCurrencyStatus = feeCurrencyStatus
|
||||
minimumTransactionAmount = minTransactionAmount
|
||||
subscribeOnQRScannerResult()
|
||||
when {
|
||||
uiState.value.sendState?.isSuccess == true -> return
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ internal class StakingAnalyticSender(
|
|||
fun initialInfoScreen(value: StakingUiState) {
|
||||
val initialInfoState = value.initialInfoState as? StakingStates.InitialInfoState.Data
|
||||
val validatorState = initialInfoState?.yieldBalance as? InnerYieldBalanceState.Data
|
||||
val validatorCount = validatorState?.balance
|
||||
val validatorCount = validatorState?.balances
|
||||
?.filterNot { it.validator?.address.isNullOrBlank() }
|
||||
?.distinctBy { it.validator?.address }
|
||||
?.size ?: 0
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ internal sealed class InnerYieldBalanceState {
|
|||
val rewardsFiat: String,
|
||||
val rewardBlockType: RewardBlockType,
|
||||
val isActionable: Boolean,
|
||||
val balance: ImmutableList<BalanceState>,
|
||||
val balances: ImmutableList<BalanceState>,
|
||||
) : InnerYieldBalanceState()
|
||||
|
||||
data object Empty : InnerYieldBalanceState()
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ internal object StakingNotification {
|
|||
) : NotificationUM.Warning(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = buttonsState,
|
||||
onCloseClick = onCloseClick,
|
||||
) {
|
||||
|
|
@ -47,6 +47,11 @@ internal object StakingNotification {
|
|||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
) : StakingNotification.Warning(title = title, subtitle = description)
|
||||
|
||||
data object LowStakedBalance : StakingNotification.Warning(
|
||||
title = resourceReference(R.string.staking_notification_low_staked_balance_title),
|
||||
subtitle = resourceReference(R.string.staking_notification_low_staked_balance_text),
|
||||
)
|
||||
}
|
||||
|
||||
sealed class Info(
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ internal class StakingStateController @Inject constructor(
|
|||
walletName = "",
|
||||
cryptoCurrencyName = "",
|
||||
cryptoCurrencySymbol = "",
|
||||
cryptoCurrencyNetworkId = "",
|
||||
cryptoCurrencyBlockchainId = "",
|
||||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = StakingStates.InitialInfoState.Empty(),
|
||||
amountState = AmountState.Empty(),
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ internal class StakingStateRouter(
|
|||
StakingStep.InitialInfo -> when (stateController.value.actionType) {
|
||||
StakingActionCommonType.Enter -> showAmount()
|
||||
// TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing
|
||||
StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyNetworkId)) {
|
||||
StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyBlockchainId)) {
|
||||
showConfirmation()
|
||||
} else {
|
||||
showAmount()
|
||||
|
|
@ -54,10 +54,15 @@ internal class StakingStateRouter(
|
|||
StakingStep.Amount,
|
||||
-> showInitial()
|
||||
StakingStep.Confirmation -> {
|
||||
if (uiState.actionType != StakingActionCommonType.Enter) {
|
||||
showInitial()
|
||||
} else {
|
||||
val isEnter = uiState.actionType == StakingActionCommonType.Enter
|
||||
val isExit = uiState.actionType == StakingActionCommonType.Exit
|
||||
|
||||
// TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing
|
||||
val isSolana = isSolana(uiState.cryptoCurrencyBlockchainId)
|
||||
if (isEnter || isExit && !isSolana) {
|
||||
showAmount()
|
||||
} else {
|
||||
showInitial()
|
||||
}
|
||||
}
|
||||
StakingStep.Validators -> showConfirmation()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ internal data class StakingUiState(
|
|||
val walletName: String,
|
||||
val cryptoCurrencyName: String,
|
||||
val cryptoCurrencySymbol: String,
|
||||
val cryptoCurrencyNetworkId: String,
|
||||
val cryptoCurrencyBlockchainId: String,
|
||||
val currentStep: StakingStep,
|
||||
val initialInfoState: StakingStates.InitialInfoState,
|
||||
val amountState: AmountState,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ internal class YieldBalancesConverter(
|
|||
},
|
||||
rewardBlockType = type,
|
||||
isActionable = isActionable,
|
||||
balance = balanceToShowItems.mapBalances(),
|
||||
balances = balanceToShowItems.mapBalances(),
|
||||
)
|
||||
} else {
|
||||
InnerYieldBalanceState.Empty
|
||||
|
|
|
|||
|
|
@ -59,9 +59,6 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
suspend fun partialUpdate() {
|
||||
coroutineScope {
|
||||
listOf(
|
||||
async {
|
||||
updateStakeBalance()
|
||||
},
|
||||
async {
|
||||
updateNetworkStatuses(delay = 0)
|
||||
},
|
||||
|
|
@ -78,6 +75,9 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
async {
|
||||
updateStakeBalance()
|
||||
},
|
||||
async {
|
||||
updateProcessingActions()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ internal object InitialStakingStatePreview {
|
|||
rewardsCrypto = "100 SOL",
|
||||
rewardBlockType = RewardBlockType.RewardUnavailable,
|
||||
isActionable = true,
|
||||
balance = persistentListOf(
|
||||
balances = persistentListOf(
|
||||
BalanceState(
|
||||
groupId = "groupId",
|
||||
title = stringReference("Binance"),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
|
||||
import com.tangem.common.ui.amountScreen.models.AmountParameters
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -11,7 +11,7 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
|||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
|
@ -21,30 +21,36 @@ internal class SetAmountDataTransformer(
|
|||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val maxEnterAmountProvider: Provider<MaxEnterAmount>,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
maxEnterAmountProvider = maxEnterAmountProvider,
|
||||
)
|
||||
}
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val title = if (prevState.actionType == StakingActionCommonType.Exit) {
|
||||
resourceReference(R.string.staking_staked_amount)
|
||||
} else {
|
||||
stringReference(userWalletProvider().name)
|
||||
}
|
||||
val cryptoBalanceValue = cryptoCurrencyStatusProvider().value
|
||||
val (amount, fiatAmount) = if (prevState.actionType != StakingActionCommonType.Enter) {
|
||||
prevState.balanceState?.cryptoAmount to prevState.balanceState?.fiatAmount
|
||||
} else {
|
||||
cryptoBalanceValue.amount to cryptoBalanceValue.fiatAmount
|
||||
}
|
||||
val maxEnterAmount = EnterAmountBoundary(
|
||||
amount = amount,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatRate = cryptoBalanceValue.fiatRate,
|
||||
)
|
||||
|
||||
return prevState.copy(
|
||||
amountState = amountStateConverter.convert(
|
||||
amountState = AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
maxEnterAmount = maxEnterAmount,
|
||||
).convert(
|
||||
AmountParameters(
|
||||
title = title,
|
||||
value = "",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.common.extensions.remove
|
|||
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
|
||||
import com.tangem.common.ui.amountScreen.models.AmountParameters
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
||||
import com.tangem.core.ui.extensions.*
|
||||
|
|
@ -41,21 +41,10 @@ internal class SetInitialDataStateTransformer(
|
|||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
||||
private val maxEnterAmountProvider: Provider<MaxEnterAmount>,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
maxEnterAmountProvider = maxEnterAmountProvider,
|
||||
)
|
||||
}
|
||||
|
||||
private val rewardsValidatorStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
RewardsValidatorStateConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield)
|
||||
}
|
||||
|
|
@ -75,7 +64,7 @@ internal class SetInitialDataStateTransformer(
|
|||
title = TextReference.EMPTY,
|
||||
cryptoCurrencyName = cryptoCurrency.name,
|
||||
cryptoCurrencySymbol = cryptoCurrency.symbol,
|
||||
cryptoCurrencyNetworkId = cryptoCurrency.network.id.value,
|
||||
cryptoCurrencyBlockchainId = cryptoCurrency.network.id.value,
|
||||
clickIntents = clickIntents,
|
||||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = createInitialInfoState(),
|
||||
|
|
@ -211,7 +200,19 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private fun createInitialAmountState(): AmountState {
|
||||
return amountStateConverter.convert(
|
||||
val cryptoBalanceValue = cryptoCurrencyStatusProvider().value
|
||||
val maxEnterAmount = EnterAmountBoundary(
|
||||
amount = cryptoBalanceValue.amount,
|
||||
fiatAmount = cryptoBalanceValue.fiatAmount,
|
||||
fiatRate = cryptoBalanceValue.fiatRate,
|
||||
)
|
||||
return AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
maxEnterAmount = maxEnterAmount,
|
||||
).convert(
|
||||
AmountParameters(
|
||||
title = stringReference(userWalletProvider().name),
|
||||
value = "",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer
|
|||
|
||||
internal class AmountChangeStateTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: String,
|
||||
private val yield: Yield,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
|
@ -20,7 +21,7 @@ internal class AmountChangeStateTransformer(
|
|||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val actionType = prevState.actionType
|
||||
val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) {
|
||||
MaxEnterAmount(
|
||||
EnterAmountBoundary(
|
||||
amount = prevState.balanceState?.cryptoAmount,
|
||||
fiatAmount = prevState.balanceState?.fiatAmount,
|
||||
fiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
|
|
@ -29,7 +30,12 @@ internal class AmountChangeStateTransformer(
|
|||
maxEnterAmountConverter.convert(cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
val updatedAmountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(prevState.amountState)
|
||||
val updatedAmountState = AmountFieldChangeTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxEnterAmount,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = value,
|
||||
).transform(prevState.amountState)
|
||||
|
||||
return prevState.copy(
|
||||
amountState = AmountRequirementStateTransformer(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer
|
|||
|
||||
internal class AmountMaxValueStateTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val actionType: StakingActionCommonType,
|
||||
private val yield: Yield,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
|
@ -19,7 +20,7 @@ internal class AmountMaxValueStateTransformer(
|
|||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) {
|
||||
MaxEnterAmount(
|
||||
EnterAmountBoundary(
|
||||
amount = prevState.balanceState?.cryptoAmount,
|
||||
fiatAmount = prevState.balanceState?.fiatAmount,
|
||||
fiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
|
|
@ -28,8 +29,11 @@ internal class AmountMaxValueStateTransformer(
|
|||
maxEnterAmountConverter.convert(cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
val updatedAmountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount)
|
||||
.transform(prevState.amountState)
|
||||
val updatedAmountState = AmountFieldSetMaxAmountTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxAmount = maxEnterAmount,
|
||||
minAmount = minimumTransactionAmount,
|
||||
).transform(prevState.amountState)
|
||||
return prevState.copy(
|
||||
amountState = AmountRequirementStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
|
|
|
|||
|
|
@ -2,18 +2,24 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
|||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class AmountReduceByStateTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: ReduceByData,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
return prevState.copy(
|
||||
amountState = AmountReduceByTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState),
|
||||
amountState = AmountReduceByTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = value,
|
||||
).transform(prevState.amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
|
@ -8,11 +9,16 @@ import java.math.BigDecimal
|
|||
|
||||
internal class AmountReduceToStateTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val minimumTransactionAmount: EnterAmountBoundary?,
|
||||
private val value: BigDecimal,
|
||||
) : Transformer<StakingUiState> {
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
return prevState.copy(
|
||||
amountState = AmountReduceToTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState),
|
||||
amountState = AmountReduceToTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = value,
|
||||
).transform(prevState.amountState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.amount
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
|
|
@ -26,25 +26,18 @@ internal class AmountRequirementStateTransformer(
|
|||
private val actionType: StakingActionCommonType,
|
||||
) : Transformer<AmountState> {
|
||||
override fun transform(prevState: AmountState): AmountState {
|
||||
val amountRequirements = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]
|
||||
|
||||
return if (prevState is AmountState.Data && amountRequirements != null) {
|
||||
return if (prevState is AmountState.Data) {
|
||||
updateWithError(
|
||||
prevState,
|
||||
actionType,
|
||||
amountRequirements,
|
||||
)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWithError(
|
||||
amountState: AmountState.Data,
|
||||
actionType: StakingActionCommonType,
|
||||
amountRequirements: AddressArgument,
|
||||
): AmountState {
|
||||
val isRequirementError = isRequirementError(amountState, amountRequirements)
|
||||
private fun updateWithError(amountState: AmountState.Data, actionType: StakingActionCommonType): AmountState {
|
||||
val requirementError = getRequirementError(amountState)
|
||||
val isIntegerOnlyError = isIntegerOnlyError(amountState, actionType)
|
||||
|
||||
val cryptoAmount = amountState.amountTextField.cryptoAmount
|
||||
|
|
@ -53,14 +46,7 @@ internal class AmountRequirementStateTransformer(
|
|||
|
||||
val errorText = when {
|
||||
amountState.amountTextField.isError -> amountState.amountTextField.error
|
||||
isRequirementError -> resourceReference(
|
||||
R.string.staking_amount_requirement_error,
|
||||
wrappedList(
|
||||
amountRequirements.minimum.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
},
|
||||
),
|
||||
)
|
||||
requirementError != null -> requirementError
|
||||
isIntegerOnlyError -> when (actionType) {
|
||||
StakingActionCommonType.Enter -> resourceReference(
|
||||
R.string.staking_amount_tron_integer_error,
|
||||
|
|
@ -70,39 +56,43 @@ internal class AmountRequirementStateTransformer(
|
|||
R.string.staking_amount_tron_integer_error_unstaking,
|
||||
wrappedList(value),
|
||||
)
|
||||
else -> TODO()
|
||||
else -> null
|
||||
}
|
||||
else -> TextReference.EMPTY
|
||||
else -> null
|
||||
}
|
||||
val isError = amountState.amountTextField.isError || isRequirementError
|
||||
return if (!errorText.isNullOrEmpty()) {
|
||||
amountState.copy(
|
||||
isPrimaryButtonEnabled = !isError,
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
isError = isError,
|
||||
isWarning = isIntegerOnlyError,
|
||||
error = errorText,
|
||||
keyboardOptions = amountState.amountTextField.keyboardOptions.copy(
|
||||
imeAction = ImeAction.None,
|
||||
),
|
||||
val isError = amountState.amountTextField.isError || requirementError != null
|
||||
return amountState.copy(
|
||||
isPrimaryButtonEnabled = !isError,
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
isError = isError,
|
||||
isWarning = isIntegerOnlyError,
|
||||
error = errorText ?: amountState.amountTextField.error,
|
||||
keyboardOptions = amountState.amountTextField.keyboardOptions.copy(
|
||||
imeAction = ImeAction.None,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
amountState
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isRequirementError(prevState: AmountState.Data, amountRequirements: AddressArgument): Boolean {
|
||||
val amountDecimal = prevState.amountTextField.cryptoAmount.value ?: return false
|
||||
private fun getRequirementError(prevState: AmountState.Data): TextReference? {
|
||||
val amountDecimal = prevState.amountTextField.cryptoAmount.value ?: return null
|
||||
|
||||
val isAlreadyErrorState = prevState.amountTextField.isError
|
||||
val isAmountRequired = amountRequirements.required
|
||||
val isAmountZero = amountDecimal.isZero()
|
||||
val isExceedsRequirements =
|
||||
amountRequirements.maximum?.compareTo(amountDecimal) == -1 ||
|
||||
amountRequirements.minimum?.compareTo(amountDecimal) == 1
|
||||
|
||||
return !isAmountZero && isAmountRequired && isExceedsRequirements && !isAlreadyErrorState
|
||||
if (isAlreadyErrorState || isAmountZero) return null
|
||||
|
||||
return when (actionType) {
|
||||
StakingActionCommonType.Enter -> {
|
||||
val enterRequirements = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]
|
||||
enterRequirements?.getError(amountDecimal, R.string.staking_amount_requirement_error)
|
||||
}
|
||||
StakingActionCommonType.Exit -> {
|
||||
val exitRequirements = yield.args.exit?.args?.get(Yield.Args.ArgType.AMOUNT)
|
||||
exitRequirements?.getError(amountDecimal, R.string.staking_unstake_amount_requirement_error)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun isIntegerOnlyError(amountState: AmountState.Data, actionType: StakingActionCommonType): Boolean {
|
||||
|
|
@ -116,6 +106,20 @@ internal class AmountRequirementStateTransformer(
|
|||
return isEnterOrExit && isTron && !isIntegerOnly
|
||||
}
|
||||
|
||||
private fun AddressArgument.getError(amount: BigDecimal, @StringRes errorTextRes: Int): TextReference? {
|
||||
val isExceedsRequirements = maximum?.compareTo(amount) == -1 ||
|
||||
minimum?.compareTo(amount) == 1
|
||||
|
||||
return resourceReference(
|
||||
errorTextRes,
|
||||
wrappedList(
|
||||
minimum.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
},
|
||||
),
|
||||
).takeIf { required && isExceedsRequirements }
|
||||
}
|
||||
|
||||
data class Data(
|
||||
val amountState: AmountState,
|
||||
val actionType: StakingActionCommonType,
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
notifications = this,
|
||||
prevState = prevState,
|
||||
sendingAmount = sendingAmount,
|
||||
actionAmount = amountValue,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
|||
import com.tangem.lib.crypto.BlockchainUtils.isCosmos
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -26,12 +27,22 @@ internal class StakingInfoNotificationsFactory(
|
|||
private val isSubtractAvailable: Boolean,
|
||||
) {
|
||||
|
||||
/**
|
||||
* @param notifications current notification to display
|
||||
* @param prevState current screen state to update
|
||||
* @param sendingAmount amount being transferred from user account
|
||||
* @param actionAmount any amount being transferred or used action
|
||||
* @param feeValue fee amount payed from user account
|
||||
*/
|
||||
fun addInfoNotifications(
|
||||
notifications: MutableList<NotificationUM>,
|
||||
prevState: StakingUiState,
|
||||
sendingAmount: BigDecimal,
|
||||
actionAmount: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
) = with(notifications) {
|
||||
addStakingLowBalanceNotification(prevState, actionAmount)
|
||||
|
||||
when (prevState.actionType) {
|
||||
StakingActionCommonType.Enter -> addEnterInfoNotifications(sendingAmount, feeValue)
|
||||
StakingActionCommonType.Exit -> addExitInfoNotifications()
|
||||
|
|
@ -75,7 +86,9 @@ internal class StakingInfoNotificationsFactory(
|
|||
resourceReference(R.string.staking_restake) to
|
||||
resourceReference(R.string.staking_notification_restake_rewards_text)
|
||||
}
|
||||
StakingActionType.WITHDRAW -> {
|
||||
StakingActionType.CLAIM_UNSTAKED,
|
||||
StakingActionType.WITHDRAW,
|
||||
-> {
|
||||
resourceReference(R.string.staking_withdraw) to
|
||||
resourceReference(R.string.staking_notification_withdraw_text)
|
||||
}
|
||||
|
|
@ -149,4 +162,21 @@ internal class StakingInfoNotificationsFactory(
|
|||
add(StakingNotification.Info.StakeEntireBalance)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addStakingLowBalanceNotification(
|
||||
prevState: StakingUiState,
|
||||
actionAmount: BigDecimal,
|
||||
) {
|
||||
if (prevState.actionType != StakingActionCommonType.Exit) return
|
||||
|
||||
val maxAmount = prevState.balanceState?.cryptoAmount ?: return
|
||||
val exitRequirements = yield.args.exit?.args?.get(Yield.Args.ArgType.AMOUNT) ?: return
|
||||
|
||||
val amountLeft = maxAmount - actionAmount
|
||||
val isNotEnoughLeft = !amountLeft.isZero() && amountLeft < exitRequirements.minimum.orZero()
|
||||
|
||||
if (exitRequirements.required && isNotEnoughLeft) {
|
||||
add(StakingNotification.Warning.LowStakedBalance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,10 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
internal fun StakingActionType?.getPendingActionTitle(): TextReference = when (this) {
|
||||
StakingActionType.CLAIM_REWARDS -> resourceReference(R.string.common_claim_rewards)
|
||||
StakingActionType.RESTAKE_REWARDS -> resourceReference(R.string.staking_restake_rewards)
|
||||
StakingActionType.WITHDRAW -> resourceReference(R.string.staking_withdraw)
|
||||
StakingActionType.CLAIM_UNSTAKED,
|
||||
StakingActionType.WITHDRAW,
|
||||
-> resourceReference(R.string.staking_withdraw)
|
||||
StakingActionType.RESTAKE -> resourceReference(R.string.staking_restake)
|
||||
StakingActionType.CLAIM_UNSTAKED -> resourceReference(R.string.staking_claim_unstaked)
|
||||
StakingActionType.UNLOCK_LOCKED -> resourceReference(R.string.staking_unlocked_locked)
|
||||
StakingActionType.STAKE_LOCKED -> resourceReference(R.string.staking_stake_locked)
|
||||
StakingActionType.VOTE -> resourceReference(R.string.staking_vote)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
|
|||
import com.tangem.features.staking.impl.presentation.ui.block.ValidatorBlock
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun StakingConfirmationContent(
|
||||
amountState: AmountState,
|
||||
|
|
@ -39,9 +40,10 @@ internal fun StakingConfirmationContent(
|
|||
validatorState: StakingStates.ValidatorState,
|
||||
clickIntents: StakingClickIntents,
|
||||
type: StakingActionCommonType,
|
||||
isSolana: Boolean, // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing
|
||||
) {
|
||||
if (state !is StakingStates.ConfirmationState.Data) return
|
||||
val isEnterAction = type == StakingActionCommonType.Enter
|
||||
val isAmountEditable = type == StakingActionCommonType.Enter || type == StakingActionCommonType.Exit && !isSolana
|
||||
val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED
|
||||
val isTransactionInProgress = state.notifications.any { it is StakingNotification.Warning.TransactionInProgress }
|
||||
Column(
|
||||
|
|
@ -63,8 +65,8 @@ internal fun StakingConfirmationContent(
|
|||
}
|
||||
AmountBlock(
|
||||
amountState = amountState,
|
||||
isClickDisabled = !isEnterAction || isTransactionSent || isTransactionInProgress,
|
||||
isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED,
|
||||
isClickDisabled = !isAmountEditable || isTransactionSent || isTransactionInProgress,
|
||||
isEditingDisabled = !isAmountEditable && state.innerState != InnerConfirmationStakingState.COMPLETED,
|
||||
onClick = clickIntents::onPrevClick,
|
||||
)
|
||||
ValidatorBlock(
|
||||
|
|
@ -89,6 +91,7 @@ private fun Preview_StakingConfirmationContent() {
|
|||
validatorState = ValidatorStatePreviewData.validatorState,
|
||||
clickIntents = StakingClickIntentsStub,
|
||||
type = StakingActionCommonType.Enter,
|
||||
isSolana = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,18 +128,20 @@ private fun LazyListScope.activeStakingBlock(
|
|||
clickIntents: StakingClickIntents,
|
||||
isBalanceHidden: Boolean,
|
||||
) {
|
||||
val balances = (state.yieldBalance as? InnerYieldBalanceState.Data)?.balance
|
||||
if (!balances.isNullOrEmpty()) {
|
||||
item(key = STAKING_REWARD_BLOCK_KEY) {
|
||||
Column(modifier = Modifier.animateItem()) {
|
||||
StakingRewardBlock(
|
||||
yieldBalanceState = state.yieldBalance,
|
||||
onRewardsClick = clickIntents::openRewardsValidators,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
SpacerH12()
|
||||
}
|
||||
val innerYieldBalanceState = state.yieldBalance as? InnerYieldBalanceState.Data ?: return
|
||||
|
||||
item(key = STAKING_REWARD_BLOCK_KEY) {
|
||||
Column(modifier = Modifier.animateItem()) {
|
||||
StakingRewardBlock(
|
||||
yieldBalanceState = state.yieldBalance,
|
||||
onRewardsClick = clickIntents::openRewardsValidators,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
SpacerH12()
|
||||
}
|
||||
}
|
||||
|
||||
if (innerYieldBalanceState.balances.isNotEmpty()) {
|
||||
item(ACTIVE_STAKING_BLOCK_KEY) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.staking_your_stakes),
|
||||
|
|
@ -148,7 +150,7 @@ private fun LazyListScope.activeStakingBlock(
|
|||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
lastIndex = 1 + state.yieldBalance.balance.lastIndex,
|
||||
lastIndex = 1 + state.yieldBalance.balances.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
|
|
@ -162,10 +164,14 @@ private fun LazyListScope.activeStakingBlock(
|
|||
)
|
||||
}
|
||||
itemsIndexed(
|
||||
items = state.yieldBalance.balance,
|
||||
key = { _, balance ->
|
||||
items = state.yieldBalance.balances,
|
||||
key = { index, balance ->
|
||||
// Staked balance does not have unique identifier.
|
||||
balance.toString()
|
||||
buildString {
|
||||
append(balance.hashCode())
|
||||
append("_")
|
||||
append(index)
|
||||
}
|
||||
},
|
||||
) { index, balance ->
|
||||
ActiveStakingBlock(
|
||||
|
|
@ -177,7 +183,7 @@ private fun LazyListScope.activeStakingBlock(
|
|||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index + 1,
|
||||
lastIndex = state.yieldBalance.balance.lastIndex + 1,
|
||||
lastIndex = state.yieldBalance.balances.lastIndex + 1,
|
||||
addDefaultPadding = false,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingAc
|
|||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig
|
||||
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet
|
||||
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -170,6 +171,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
|
|||
validatorState = uiState.validatorState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
type = uiState.actionType,
|
||||
isSolana = isSolana(uiState.cryptoCurrencyBlockchainId),
|
||||
)
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.Validators,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
|
|
@ -37,6 +37,7 @@ import com.tangem.domain.staking.model.StakingApproval
|
|||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -73,6 +74,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstake
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -93,6 +95,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
|
|
@ -140,6 +143,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var processingActions: List<StakingAction> = emptyList()
|
||||
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var minimumTransactionAmount: EnterAmountBoundary? = null
|
||||
|
||||
private var innerRouter: InnerStakingRouter by Delegates.notNull()
|
||||
private var userWallet: UserWallet by Delegates.notNull()
|
||||
|
|
@ -154,14 +158,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
).getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
private val maxEnterAmount: MaxEnterAmount
|
||||
get() =
|
||||
MaxEnterAmount(
|
||||
amount = uiState.value.balanceState?.cryptoAmount,
|
||||
fiatAmount = uiState.value.balanceState?.fiatAmount,
|
||||
fiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
)
|
||||
|
||||
private var isInitialInfoAnalyticSent: Boolean = false
|
||||
|
||||
private val balanceUpdater by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -403,7 +399,14 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onAmountValueChange(value: String) {
|
||||
stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, value, yield))
|
||||
stateController.update(
|
||||
AmountChangeStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = value,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {
|
||||
|
|
@ -415,6 +418,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(
|
||||
AmountMaxValueStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
actionType = uiState.value.actionType,
|
||||
yield = yield,
|
||||
),
|
||||
|
|
@ -658,6 +662,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
) {
|
||||
AmountReduceByStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = AmountReduceByTransformer.ReduceByData(
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
reduceAmountByDiff = reduceAmountByDiff,
|
||||
|
|
@ -670,6 +675,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(
|
||||
AmountReduceToStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
value = reduceAmountTo,
|
||||
),
|
||||
)
|
||||
|
|
@ -833,6 +839,13 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
feeCryptoCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
|
||||
minimumTransactionAmount =
|
||||
getMinimumTransactionAmountSyncUseCase(userWalletId, status).getOrNull()?.let {
|
||||
EnterAmountBoundary(
|
||||
amount = it,
|
||||
fiatRate = status.value.fiatRate.orZero(),
|
||||
)
|
||||
}
|
||||
cryptoCurrencyStatus = status
|
||||
|
||||
setupApprovalNeeded()
|
||||
|
|
@ -929,7 +942,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
maxEnterAmountProvider = Provider { maxEnterAmount },
|
||||
),
|
||||
SetConfirmationStateEmptyTransformer,
|
||||
)
|
||||
|
|
@ -946,7 +958,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.updateAll(
|
||||
SetConfirmationStateInitTransformer(
|
||||
isEnter = false,
|
||||
isExplicitExit = balanceType == BalanceType.STAKED,
|
||||
isExplicitExit = isExplicitExit(balanceType, pendingAction),
|
||||
balanceState = balanceState,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
stakingApproval = stakingApproval,
|
||||
|
|
@ -963,16 +975,20 @@ internal class StakingViewModel @Inject constructor(
|
|||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
maxEnterAmountProvider = Provider { maxEnterAmount },
|
||||
),
|
||||
AmountChangeStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
value = amountValue,
|
||||
minimumTransactionAmount = minimumTransactionAmount,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isExplicitExit(balanceType: BalanceType, pendingAction: PendingAction?): Boolean {
|
||||
return balanceType == BalanceType.STAKED && pendingAction?.type != StakingActionType.RESTAKE
|
||||
}
|
||||
|
||||
private fun isAssentState(): Boolean {
|
||||
return value.currentStep == StakingStep.Confirmation &&
|
||||
(value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState ==
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import arrow.core.getOrElse
|
|||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain.*
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
|
|
@ -45,7 +46,6 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
|
@ -865,8 +865,8 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
} else {
|
||||
when (blockchain) {
|
||||
// region Blockchains with their own fees
|
||||
Blockchain.VeChain,
|
||||
Blockchain.VeChainTestnet,
|
||||
VeChain,
|
||||
VeChainTestnet,
|
||||
-> {
|
||||
Fee.VeChain(
|
||||
amount = feeAmount,
|
||||
|
|
@ -874,28 +874,28 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
gasLimit = fee.gasLimit.toLong(),
|
||||
)
|
||||
}
|
||||
Blockchain.Aptos,
|
||||
Blockchain.AptosTestnet,
|
||||
Aptos,
|
||||
AptosTestnet,
|
||||
-> {
|
||||
val gasUnitPrice = fee.feeValue.divide(
|
||||
fee.gasLimit.toBigDecimal(),
|
||||
Blockchain.Aptos.decimals(),
|
||||
Aptos.decimals(),
|
||||
RoundingMode.HALF_UP,
|
||||
)
|
||||
|
||||
Fee.Aptos(
|
||||
amount = feeAmount,
|
||||
gasUnitPrice = gasUnitPrice
|
||||
.movePointRight(Blockchain.Aptos.decimals())
|
||||
.movePointRight(Aptos.decimals())
|
||||
.toLong(),
|
||||
gasLimit = fee.gasLimit.toLong(),
|
||||
)
|
||||
}
|
||||
Blockchain.Filecoin,
|
||||
Filecoin,
|
||||
-> {
|
||||
val gasUnitPrice = fee.feeValue.divide(
|
||||
BigDecimal(fee.gasLimit),
|
||||
Blockchain.Filecoin.decimals(),
|
||||
Filecoin.decimals(),
|
||||
RoundingMode.HALF_UP,
|
||||
)
|
||||
val feeParams = requireNotNull(fee.params as? TxFee.Params.Filecoin)
|
||||
|
|
@ -903,14 +903,14 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
Fee.Filecoin(
|
||||
amount = feeAmount,
|
||||
gasUnitPrice = gasUnitPrice
|
||||
.movePointRight(Blockchain.Filecoin.decimals())
|
||||
.movePointRight(Filecoin.decimals())
|
||||
.toLong(),
|
||||
gasLimit = fee.gasLimit.toLong(),
|
||||
gasPremium = feeParams.gasPremium,
|
||||
)
|
||||
}
|
||||
Blockchain.Sui,
|
||||
Blockchain.SuiTestnet,
|
||||
Sui,
|
||||
SuiTestnet,
|
||||
-> {
|
||||
val feeParams = requireNotNull(fee.params as? TxFee.Params.Sui)
|
||||
|
||||
|
|
@ -922,133 +922,133 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
}
|
||||
// endregion
|
||||
// region Blockchains with common fees or EVM-like fees
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Arbitrum,
|
||||
Blockchain.ArbitrumTestnet,
|
||||
Blockchain.Avalanche,
|
||||
Blockchain.AvalancheTestnet,
|
||||
Blockchain.Binance,
|
||||
Blockchain.BinanceTestnet,
|
||||
Blockchain.BSC,
|
||||
Blockchain.BSCTestnet,
|
||||
Blockchain.Bitcoin,
|
||||
Blockchain.BitcoinTestnet,
|
||||
Blockchain.BitcoinCash,
|
||||
Blockchain.BitcoinCashTestnet,
|
||||
Blockchain.Cardano,
|
||||
Blockchain.Cosmos,
|
||||
Blockchain.CosmosTestnet,
|
||||
Blockchain.Dogecoin,
|
||||
Blockchain.Ducatus,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.EthereumTestnet,
|
||||
Blockchain.EthereumClassic,
|
||||
Blockchain.EthereumClassicTestnet,
|
||||
Blockchain.Fantom,
|
||||
Blockchain.FantomTestnet,
|
||||
Blockchain.Litecoin,
|
||||
Blockchain.Near,
|
||||
Blockchain.NearTestnet,
|
||||
Blockchain.Polkadot,
|
||||
Blockchain.PolkadotTestnet,
|
||||
Blockchain.Kava,
|
||||
Blockchain.KavaTestnet,
|
||||
Blockchain.Kusama,
|
||||
Blockchain.Polygon,
|
||||
Blockchain.PolygonTestnet,
|
||||
Blockchain.RSK,
|
||||
Blockchain.Sei,
|
||||
Blockchain.SeiTestnet,
|
||||
Blockchain.Stellar,
|
||||
Blockchain.StellarTestnet,
|
||||
Blockchain.Solana,
|
||||
Blockchain.SolanaTestnet,
|
||||
Blockchain.Tezos,
|
||||
Blockchain.Tron,
|
||||
Blockchain.TronTestnet,
|
||||
Blockchain.XRP,
|
||||
Blockchain.Gnosis,
|
||||
Blockchain.Dash,
|
||||
Blockchain.Optimism,
|
||||
Blockchain.OptimismTestnet,
|
||||
Blockchain.Dischain,
|
||||
Blockchain.EthereumPow,
|
||||
Blockchain.EthereumPowTestnet,
|
||||
Blockchain.Kaspa,
|
||||
Blockchain.Telos,
|
||||
Blockchain.TelosTestnet,
|
||||
Blockchain.TON,
|
||||
Blockchain.TONTestnet,
|
||||
Blockchain.Ravencoin,
|
||||
Blockchain.RavencoinTestnet,
|
||||
Blockchain.TerraV1,
|
||||
Blockchain.TerraV2,
|
||||
Blockchain.Cronos,
|
||||
Blockchain.AlephZero,
|
||||
Blockchain.AlephZeroTestnet,
|
||||
Blockchain.OctaSpace,
|
||||
Blockchain.OctaSpaceTestnet,
|
||||
Blockchain.Chia,
|
||||
Blockchain.ChiaTestnet,
|
||||
Blockchain.Decimal,
|
||||
Blockchain.DecimalTestnet,
|
||||
Blockchain.XDC,
|
||||
Blockchain.XDCTestnet,
|
||||
Blockchain.Playa3ull,
|
||||
Blockchain.Shibarium,
|
||||
Blockchain.ShibariumTestnet,
|
||||
Blockchain.Algorand,
|
||||
Blockchain.AlgorandTestnet,
|
||||
Blockchain.Hedera,
|
||||
Blockchain.HederaTestnet,
|
||||
Blockchain.Aurora,
|
||||
Blockchain.AuroraTestnet,
|
||||
Blockchain.Areon,
|
||||
Blockchain.AreonTestnet,
|
||||
Blockchain.PulseChain,
|
||||
Blockchain.PulseChainTestnet,
|
||||
Blockchain.ZkSyncEra,
|
||||
Blockchain.ZkSyncEraTestnet,
|
||||
Blockchain.Nexa,
|
||||
Blockchain.NexaTestnet,
|
||||
Blockchain.Moonbeam,
|
||||
Blockchain.MoonbeamTestnet,
|
||||
Blockchain.Manta,
|
||||
Blockchain.MantaTestnet,
|
||||
Blockchain.PolygonZkEVM,
|
||||
Blockchain.PolygonZkEVMTestnet,
|
||||
Blockchain.Radiant,
|
||||
Blockchain.Base,
|
||||
Blockchain.BaseTestnet,
|
||||
Blockchain.Moonriver,
|
||||
Blockchain.MoonriverTestnet,
|
||||
Blockchain.Mantle,
|
||||
Blockchain.MantleTestnet,
|
||||
Blockchain.Flare,
|
||||
Blockchain.FlareTestnet,
|
||||
Blockchain.Taraxa,
|
||||
Blockchain.TaraxaTestnet,
|
||||
Blockchain.Koinos,
|
||||
Blockchain.KoinosTestnet,
|
||||
Blockchain.Joystream,
|
||||
Blockchain.Bittensor,
|
||||
Blockchain.Blast,
|
||||
Blockchain.BlastTestnet,
|
||||
Blockchain.Cyber,
|
||||
Blockchain.CyberTestnet,
|
||||
Blockchain.InternetComputer,
|
||||
Blockchain.EnergyWebChain,
|
||||
Blockchain.EnergyWebChainTestnet,
|
||||
Blockchain.EnergyWebX,
|
||||
Blockchain.EnergyWebXTestnet,
|
||||
Blockchain.Casper,
|
||||
Blockchain.CasperTestnet,
|
||||
Blockchain.Core,
|
||||
Blockchain.CoreTestnet,
|
||||
Blockchain.Chiliz,
|
||||
Blockchain.ChilizTestnet,
|
||||
Blockchain.Xodex,
|
||||
Blockchain.Canxium,
|
||||
Unknown,
|
||||
Arbitrum,
|
||||
ArbitrumTestnet,
|
||||
Avalanche,
|
||||
AvalancheTestnet,
|
||||
Binance,
|
||||
BinanceTestnet,
|
||||
BSC,
|
||||
BSCTestnet,
|
||||
Bitcoin,
|
||||
BitcoinTestnet,
|
||||
BitcoinCash,
|
||||
BitcoinCashTestnet,
|
||||
Cardano,
|
||||
Cosmos,
|
||||
CosmosTestnet,
|
||||
Dogecoin,
|
||||
Ducatus,
|
||||
Ethereum,
|
||||
EthereumTestnet,
|
||||
EthereumClassic,
|
||||
EthereumClassicTestnet,
|
||||
Fantom,
|
||||
FantomTestnet,
|
||||
Litecoin,
|
||||
Near,
|
||||
NearTestnet,
|
||||
Polkadot,
|
||||
PolkadotTestnet,
|
||||
Kava,
|
||||
KavaTestnet,
|
||||
Kusama,
|
||||
Polygon,
|
||||
PolygonTestnet,
|
||||
RSK,
|
||||
Sei,
|
||||
SeiTestnet,
|
||||
Stellar,
|
||||
StellarTestnet,
|
||||
Solana,
|
||||
SolanaTestnet,
|
||||
Tezos,
|
||||
Tron,
|
||||
TronTestnet,
|
||||
XRP,
|
||||
Gnosis,
|
||||
Dash,
|
||||
Optimism,
|
||||
OptimismTestnet,
|
||||
Dischain,
|
||||
EthereumPow,
|
||||
EthereumPowTestnet,
|
||||
Kaspa,
|
||||
Telos,
|
||||
TelosTestnet,
|
||||
TON,
|
||||
TONTestnet,
|
||||
Ravencoin,
|
||||
RavencoinTestnet,
|
||||
TerraV1,
|
||||
TerraV2,
|
||||
Cronos,
|
||||
AlephZero,
|
||||
AlephZeroTestnet,
|
||||
OctaSpace,
|
||||
OctaSpaceTestnet,
|
||||
Chia,
|
||||
ChiaTestnet,
|
||||
Decimal,
|
||||
DecimalTestnet,
|
||||
XDC,
|
||||
XDCTestnet,
|
||||
Playa3ull,
|
||||
Shibarium,
|
||||
ShibariumTestnet,
|
||||
Algorand,
|
||||
AlgorandTestnet,
|
||||
Hedera,
|
||||
HederaTestnet,
|
||||
Aurora,
|
||||
AuroraTestnet,
|
||||
Areon,
|
||||
AreonTestnet,
|
||||
PulseChain,
|
||||
PulseChainTestnet,
|
||||
ZkSyncEra,
|
||||
ZkSyncEraTestnet,
|
||||
Nexa,
|
||||
NexaTestnet,
|
||||
Moonbeam,
|
||||
MoonbeamTestnet,
|
||||
Manta,
|
||||
MantaTestnet,
|
||||
PolygonZkEVM,
|
||||
PolygonZkEVMTestnet,
|
||||
Radiant,
|
||||
Base,
|
||||
BaseTestnet,
|
||||
Moonriver,
|
||||
MoonriverTestnet,
|
||||
Mantle,
|
||||
MantleTestnet,
|
||||
Flare,
|
||||
FlareTestnet,
|
||||
Taraxa,
|
||||
TaraxaTestnet,
|
||||
Koinos,
|
||||
KoinosTestnet,
|
||||
Joystream,
|
||||
Bittensor,
|
||||
Blast,
|
||||
BlastTestnet,
|
||||
Cyber,
|
||||
CyberTestnet,
|
||||
InternetComputer,
|
||||
EnergyWebChain,
|
||||
EnergyWebChainTestnet,
|
||||
EnergyWebX,
|
||||
EnergyWebXTestnet,
|
||||
Casper,
|
||||
CasperTestnet,
|
||||
Core,
|
||||
CoreTestnet,
|
||||
Xodex,
|
||||
Canxium,
|
||||
Chiliz,
|
||||
ChilizTestnet,
|
||||
-> Fee.Common(feeAmount)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -1594,12 +1594,25 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
amount: BigDecimal,
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Either<GetFeeError, TransactionFee>? {
|
||||
): Either<GetFeeError, TransactionFee> {
|
||||
return estimateFeeUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
).firstOrNull()
|
||||
).mapFeeToForceSingleFeeIfNeeded(cryptoCurrency)
|
||||
}
|
||||
|
||||
private fun Either<GetFeeError, TransactionFee>.mapFeeToForceSingleFeeIfNeeded(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Either<GetFeeError, TransactionFee> {
|
||||
return this.map {
|
||||
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId)
|
||||
if (it is TransactionFee.Choosable && forceSingleFeeBlockchains.contains(blockchain)) {
|
||||
TransactionFee.Single(normal = it.normal)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
|
|
@ -2213,6 +2226,17 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
private val minDemoFee = "0.0001".toBigDecimal()
|
||||
private val normalDemoFee = "0.0002".toBigDecimal()
|
||||
private val priorityDemoFee = "0.0003".toBigDecimal()
|
||||
|
||||
private val forceSingleFeeBlockchains = listOf(
|
||||
Bitcoin, BitcoinTestnet,
|
||||
BitcoinCash, BitcoinCashTestnet,
|
||||
Litecoin,
|
||||
Dogecoin,
|
||||
Dash,
|
||||
Kaspa,
|
||||
Ravencoin, RavencoinTestnet,
|
||||
Ducatus,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -176,10 +176,15 @@ internal class StateBuilder(
|
|||
val canSelectReceiveToken = mainTokenId != toToken.id.value
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
val sendInput = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable).copy(
|
||||
isError = false,
|
||||
header = TextReference.Res(R.string.swapping_from_title),
|
||||
)
|
||||
val sendInputType = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable)
|
||||
val sendInput = if (sendInputType.isError) {
|
||||
sendInputType
|
||||
} else {
|
||||
sendInputType.copy(
|
||||
isError = false,
|
||||
header = TextReference.Res(R.string.swapping_from_title),
|
||||
)
|
||||
}
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = sendInput,
|
||||
|
|
@ -256,10 +261,16 @@ internal class StateBuilder(
|
|||
} else {
|
||||
TextReference.Res(R.string.swapping_from_title)
|
||||
}
|
||||
val sendInput = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable).copy(
|
||||
isError = isInsufficientFunds,
|
||||
header = insufficientFundsHeader,
|
||||
)
|
||||
val sendCardType = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable)
|
||||
val sendInput = if (sendCardType.isError && !isInsufficientFunds) {
|
||||
// if any error in inputField and funds enough -> show that error else show fund is not enough error
|
||||
sendCardType
|
||||
} else {
|
||||
sendCardType.copy(
|
||||
isError = isInsufficientFunds,
|
||||
header = insufficientFundsHeader,
|
||||
)
|
||||
}
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = sendInput,
|
||||
|
|
@ -867,14 +878,36 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
fun updateSwapAmount(uiState: SwapStateHolder, amount: String): SwapStateHolder {
|
||||
fun updateSwapAmount(
|
||||
uiState: SwapStateHolder,
|
||||
amountFormatted: String,
|
||||
amountRaw: String,
|
||||
fromToken: CryptoCurrency,
|
||||
minTxAmount: BigDecimal?,
|
||||
): SwapStateHolder {
|
||||
if (uiState.sendCardData !is SwapCardState.SwapCardData) return uiState
|
||||
val amountToSend = amountRaw.toBigDecimalOrNull()
|
||||
val sendInput = if (minTxAmount != null && amountToSend != null && amountToSend < minTxAmount) {
|
||||
val minAmountFormatted = minTxAmount.format {
|
||||
crypto(cryptoCurrency = fromToken, ignoreSymbolPosition = true)
|
||||
}
|
||||
(uiState.sendCardData.type as? TransactionCardType.Inputtable)?.copy(
|
||||
isError = true,
|
||||
header = resourceReference(R.string.transfer_min_amount_error, wrappedList(minAmountFormatted)),
|
||||
) ?: uiState.sendCardData.type
|
||||
} else {
|
||||
(uiState.sendCardData.type as? TransactionCardType.Inputtable)?.copy(
|
||||
isError = false,
|
||||
header = TextReference.Res(R.string.swapping_from_title),
|
||||
) ?: uiState.sendCardData.type
|
||||
}
|
||||
return uiState.copy(
|
||||
sendCardData = uiState.sendCardData.copy(
|
||||
amountTextFieldValue = TextFieldValue(
|
||||
text = amount,
|
||||
selection = TextRange(amount.length),
|
||||
text = amountFormatted,
|
||||
selection = TextRange(amountFormatted.length),
|
||||
),
|
||||
type = sendInput,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1098,10 +1131,20 @@ internal class StateBuilder(
|
|||
provider: SwapProvider,
|
||||
onDismiss: () -> Unit,
|
||||
): SwapStateHolder {
|
||||
val slippage = provider.slippage?.let { "${it.parseBigDecimal(1)}$PERCENT" }
|
||||
val combinedMessage = buildList {
|
||||
when (provider.type) {
|
||||
ExchangeProviderType.CEX -> {
|
||||
add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)))
|
||||
if (slippage != null) {
|
||||
add(
|
||||
resourceReference(
|
||||
id = R.string.swapping_alert_cex_description_with_slippage,
|
||||
formatArgs = wrappedList(token, slippage),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)))
|
||||
}
|
||||
}
|
||||
ExchangeProviderType.DEX,
|
||||
ExchangeProviderType.DEX_BRIDGE,
|
||||
|
|
@ -1110,18 +1153,18 @@ internal class StateBuilder(
|
|||
add(resourceReference(R.string.swapping_high_price_impact_description))
|
||||
add(stringReference("\n\n"))
|
||||
}
|
||||
add(resourceReference(R.string.swapping_alert_dex_description))
|
||||
if (slippage != null) {
|
||||
add(
|
||||
resourceReference(
|
||||
id = R.string.swapping_alert_dex_description_with_slippage,
|
||||
formatArgs = wrappedList(token, slippage),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
add(resourceReference(R.string.swapping_alert_dex_description, wrappedList(token)))
|
||||
}
|
||||
}
|
||||
}
|
||||
provider.slippage?.let { slippage ->
|
||||
add(stringReference("\n\n"))
|
||||
add(
|
||||
resourceReference(
|
||||
R.string.swapping_alert_slippage_description,
|
||||
wrappedList("${slippage.parseBigDecimal(1)}$PERCENT"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
return uiState.copy(
|
||||
event = triggeredEvent(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
|||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
|
||||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -84,6 +85,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
swapInteractorFactory: SwapInteractor.Factory,
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
|
|
@ -866,60 +868,78 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onChangeCardsClicked() {
|
||||
val newFromToken = dataState.toCryptoCurrency
|
||||
val newToToken = dataState.fromCryptoCurrency
|
||||
viewModelScope.launch {
|
||||
val newFromToken = dataState.toCryptoCurrency
|
||||
val newToToken = dataState.fromCryptoCurrency
|
||||
|
||||
if (newFromToken != null && newToToken != null) {
|
||||
isAmountChangedByUser = true
|
||||
if (newFromToken != null && newToToken != null) {
|
||||
isAmountChangedByUser = true
|
||||
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = newFromToken,
|
||||
toCryptoCurrency = newToToken,
|
||||
)
|
||||
isOrderReversed = !isOrderReversed
|
||||
dataState.tokensDataState?.let {
|
||||
updateTokensState(it)
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = newFromToken,
|
||||
toCryptoCurrency = newToToken,
|
||||
)
|
||||
isOrderReversed = !isOrderReversed
|
||||
dataState.tokensDataState?.let {
|
||||
updateTokensState(it)
|
||||
}
|
||||
|
||||
val minTxAmount = getMinimumTransactionAmountSyncUseCase(
|
||||
userWalletId,
|
||||
newFromToken,
|
||||
).getOrNull()
|
||||
val decimals = newFromToken.currency.decimals
|
||||
lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value)
|
||||
uiState = stateBuilder.updateSwapAmount(
|
||||
uiState = uiState,
|
||||
amountFormatted = inputNumberFormatter.formatWithThousands(lastAmount.value, decimals),
|
||||
amountRaw = lastAmount.value,
|
||||
fromToken = newFromToken.currency,
|
||||
minTxAmount = minTxAmount,
|
||||
)
|
||||
startLoadingQuotes(
|
||||
fromToken = newFromToken,
|
||||
toToken = newToToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(newFromToken, newToToken),
|
||||
)
|
||||
}
|
||||
|
||||
val decimals = newFromToken.currency.decimals
|
||||
lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value)
|
||||
uiState = stateBuilder.updateSwapAmount(
|
||||
uiState,
|
||||
inputNumberFormatter.formatWithThousands(lastAmount.value, decimals),
|
||||
)
|
||||
startLoadingQuotes(
|
||||
fromToken = newFromToken,
|
||||
toToken = newToToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(newFromToken, newToToken),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAmountChanged(value: String) {
|
||||
val fromToken = dataState.fromCryptoCurrency
|
||||
val toToken = dataState.toCryptoCurrency
|
||||
if (fromToken != null) {
|
||||
val decimals = fromToken.currency.decimals
|
||||
val cutValue = cutAmountWithDecimals(decimals, value)
|
||||
lastAmount.value = cutValue
|
||||
uiState = stateBuilder.updateSwapAmount(
|
||||
uiState = uiState,
|
||||
amount = inputNumberFormatter.formatWithThousands(cutValue, decimals),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
val fromToken = dataState.fromCryptoCurrency
|
||||
val toToken = dataState.toCryptoCurrency
|
||||
if (fromToken != null) {
|
||||
val decimals = fromToken.currency.decimals
|
||||
val cutValue = cutAmountWithDecimals(decimals, value)
|
||||
val minTxAmount = getMinimumTransactionAmountSyncUseCase(
|
||||
userWalletId,
|
||||
fromToken,
|
||||
).getOrNull()
|
||||
lastAmount.value = cutValue
|
||||
uiState = stateBuilder.updateSwapAmount(
|
||||
uiState = uiState,
|
||||
amountFormatted = inputNumberFormatter.formatWithThousands(cutValue, decimals),
|
||||
amountRaw = lastAmount.value,
|
||||
fromToken = fromToken.currency,
|
||||
minTxAmount = minTxAmount,
|
||||
)
|
||||
|
||||
if (toToken != null) {
|
||||
if (toToken.value.amount != null) {
|
||||
isAmountChangedByUser = true
|
||||
}
|
||||
if (toToken != null) {
|
||||
if (toToken.value.amount != null) {
|
||||
isAmountChangedByUser = true
|
||||
}
|
||||
|
||||
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(fromToken, toToken),
|
||||
)
|
||||
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
amount = lastAmount.value,
|
||||
toProvidersList = findSwapProviders(fromToken, toToken),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.core.ui.format.bigdecimal.*
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.RewardBlockType
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
|
|
@ -40,7 +39,6 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
private val symbol: String,
|
||||
private val decimals: Int,
|
||||
|
|
@ -141,53 +139,51 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
}
|
||||
|
||||
private fun getYieldBalance(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? {
|
||||
return when (stakingAvailabilityProvider.invoke()) {
|
||||
StakingAvailability.TemporaryUnavailable -> StakingBlockUM.TemporaryUnavailable
|
||||
StakingAvailability.Unavailable -> null
|
||||
is StakingAvailability.Available -> getStakingInfoBlock(status, state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStakingInfoBlock(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? {
|
||||
val yieldBalance = status.value.yieldBalance as? YieldBalance.Data
|
||||
|
||||
val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance()
|
||||
val pendingBalances = yieldBalance?.balance?.items ?: emptyList()
|
||||
|
||||
val stakingEntryInfo = stakingEntryInfoProvider.invoke()
|
||||
val stakingAvailability = stakingAvailabilityProvider.invoke()
|
||||
val iconState = state.tokenInfoBlockState.iconState
|
||||
val pendingBalances = pendingBalancesProvider.invoke()
|
||||
val fiatRate = status.value.fiatRate
|
||||
|
||||
return when {
|
||||
stakingAvailability == StakingAvailability.TemporaryUnavailable -> {
|
||||
StakingBlockUM.TemporaryUnavailable
|
||||
}
|
||||
stakingAvailability == StakingAvailability.Unavailable -> {
|
||||
null
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isEmpty() -> {
|
||||
getStakeAvailableState(stakingEntryInfo, iconState)
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isNotEmpty() -> {
|
||||
val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount }
|
||||
|
||||
val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount)
|
||||
getStakedState(
|
||||
status = status,
|
||||
stakingCryptoAmount = pendingBalancesCryptoAmount,
|
||||
stakingFiatAmount = stakingFiatAmount,
|
||||
stakingRewardAmount = null,
|
||||
)
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
|
||||
if (pendingBalances.isEmpty()) {
|
||||
getStakeAvailableState(stakingEntryInfo, iconState)
|
||||
} else {
|
||||
getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null)
|
||||
}
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()?.let { fiatRate?.multiply(it) }
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { fiatRate?.multiply(it) }
|
||||
|
||||
getStakedState(
|
||||
status = status,
|
||||
stakingCryptoAmount = stakingCryptoAmount,
|
||||
stakingFiatAmount = stakingFiatAmount,
|
||||
stakingRewardAmount = stakingRewardAmount,
|
||||
)
|
||||
}
|
||||
else -> getStakedBlockWithFiatAmount(status, stakingCryptoAmount, yieldBalance?.getRewardStakingBalance())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStakedBlockWithFiatAmount(
|
||||
status: CryptoCurrencyStatus,
|
||||
stakingAmount: BigDecimal?,
|
||||
rewardAmount: BigDecimal?,
|
||||
): StakingBlockUM.Staked {
|
||||
val fiatRate = status.value.fiatRate
|
||||
return getStakedState(
|
||||
status = status,
|
||||
stakingCryptoAmount = stakingAmount,
|
||||
stakingFiatAmount = stakingAmount?.let { fiatRate?.multiply(it) },
|
||||
stakingRewardAmount = rewardAmount?.let { fiatRate?.multiply(it) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun getMarketPriceState(
|
||||
status: CryptoCurrencyStatus.Value,
|
||||
currencySymbol: String,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.card.NetworkHasDerivationUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -32,7 +30,6 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
|
||||
private val getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
|
||||
private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val userWalletId: UserWalletId,
|
||||
) : Converter<CryptoCurrency, TokenDetailsState> {
|
||||
|
|
@ -42,8 +39,6 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
override fun convert(value: CryptoCurrency): TokenDetailsState {
|
||||
val iconState = iconStateConverter.convert(value)
|
||||
val isSupportedInMobileApp = getStakingIntegrationIdUseCase(value.id).isNullOrBlank().not()
|
||||
val stakingAvailability = getStakingAvailabilityUseCase(userWalletId, value)
|
||||
.getOrElse { StakingAvailability.Unavailable }
|
||||
|
||||
return TokenDetailsState(
|
||||
topAppBarConfig = TokenDetailsTopAppBarConfig(
|
||||
|
|
@ -68,11 +63,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
selectedBalanceType = BalanceType.ALL,
|
||||
),
|
||||
marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol),
|
||||
stakingBlocksState = if (stakingAvailability == StakingAvailability.TemporaryUnavailable) {
|
||||
StakingBlockUM.TemporaryUnavailable
|
||||
} else {
|
||||
StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp }
|
||||
},
|
||||
stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp },
|
||||
notifications = persistentListOf(),
|
||||
pendingTxs = persistentListOf(),
|
||||
swapTxs = persistentListOf(),
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.NetworkHasDerivationUseCase
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
|
|
@ -53,13 +51,11 @@ internal class TokenDetailsStateFactory(
|
|||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val userWalletId: UserWalletId,
|
||||
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
|
||||
getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
|
||||
symbol: String,
|
||||
decimals: Int,
|
||||
) {
|
||||
|
|
@ -69,7 +65,6 @@ internal class TokenDetailsStateFactory(
|
|||
clickIntents = clickIntents,
|
||||
networkHasDerivationUseCase = networkHasDerivationUseCase,
|
||||
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
|
||||
getStakingAvailabilityUseCase = getStakingAvailabilityUseCase,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
|
|
@ -85,7 +80,6 @@ internal class TokenDetailsStateFactory(
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
stakingEntryInfoProvider = stakingEntryInfoProvider,
|
||||
stakingAvailabilityProvider = stakingAvailabilityProvider,
|
||||
pendingBalancesProvider = pendingBalancesProvider,
|
||||
symbol = symbol,
|
||||
decimals = decimals,
|
||||
clickIntents = clickIntents,
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ internal class TokenDetailsTxHistoryTransactionStateConverter(
|
|||
} else {
|
||||
when (type) {
|
||||
is TransactionType.Approve -> R.drawable.ic_doc_24
|
||||
is TransactionType.TronStakingTransactionType.Stake,
|
||||
is TransactionType.TronStakingTransactionType.Vote,
|
||||
is TransactionType.Staking.Stake,
|
||||
is TransactionType.Staking.Vote,
|
||||
is TransactionType.Staking.Restake,
|
||||
-> R.drawable.ic_transaction_history_staking_24
|
||||
is TransactionType.TronStakingTransactionType.ClaimRewards,
|
||||
is TransactionType.Staking.ClaimRewards,
|
||||
-> R.drawable.ic_transaction_history_claim_rewards_24
|
||||
is TransactionType.TronStakingTransactionType.Unstake,
|
||||
is TransactionType.TronStakingTransactionType.Withdraw,
|
||||
is TransactionType.Staking.Unstake,
|
||||
is TransactionType.Staking.Withdraw,
|
||||
-> R.drawable.ic_transaction_history_unstaking_24
|
||||
is TransactionType.Operation,
|
||||
is TransactionType.Swap,
|
||||
|
|
@ -72,11 +73,12 @@ internal class TokenDetailsTxHistoryTransactionStateConverter(
|
|||
is TransactionType.Swap -> resourceReference(R.string.common_swap)
|
||||
is TransactionType.Transfer -> resourceReference(R.string.common_transfer)
|
||||
is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation)
|
||||
is TransactionType.TronStakingTransactionType.Stake -> resourceReference(R.string.common_stake)
|
||||
is TransactionType.TronStakingTransactionType.Unstake -> resourceReference(R.string.common_unstake)
|
||||
is TransactionType.TronStakingTransactionType.Vote -> resourceReference(R.string.staking_vote)
|
||||
is TransactionType.TronStakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
|
||||
is TransactionType.TronStakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw)
|
||||
is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake)
|
||||
is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake)
|
||||
is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote)
|
||||
is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
|
||||
is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw)
|
||||
is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake)
|
||||
}
|
||||
|
||||
private fun TxHistoryItem.extractSubtitle(): TextReference =
|
||||
|
|
@ -119,9 +121,9 @@ internal class TokenDetailsTxHistoryTransactionStateConverter(
|
|||
}
|
||||
|
||||
private fun TxHistoryItem.getAmount(): String {
|
||||
if (type is TransactionType.TronStakingTransactionType.Vote ||
|
||||
type == TransactionType.TronStakingTransactionType.ClaimRewards ||
|
||||
type == TransactionType.TronStakingTransactionType.Withdraw
|
||||
if (type is TransactionType.Staking.Vote ||
|
||||
type == TransactionType.Staking.ClaimRewards ||
|
||||
type == TransactionType.Staking.Withdraw
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
|||
import com.tangem.domain.staking.GetYieldUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
|
|
@ -147,7 +146,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val warningsJobHolder = JobHolder()
|
||||
private val swapTxJobHolder = JobHolder()
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
private val stakingPendingBalances: List<BalanceItem> = emptyList() // TODO staking
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var stakingEntryInfo: StakingEntryInfo? = null
|
||||
|
|
@ -160,13 +158,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
stakingEntryInfoProvider = Provider { stakingEntryInfo },
|
||||
stakingAvailabilityProvider = Provider { stakingAvailability },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
pendingBalancesProvider = Provider { stakingPendingBalances },
|
||||
clickIntents = this,
|
||||
networkHasDerivationUseCase = networkHasDerivationUseCase,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
userWalletId = userWalletId,
|
||||
getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase,
|
||||
getStakingAvailabilityUseCase = getStakingAvailabilityUseCase,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -49,13 +49,14 @@ internal class TxHistoryItemStateConverter(
|
|||
} else {
|
||||
when (type) {
|
||||
is TransactionType.Approve -> R.drawable.ic_doc_24
|
||||
is TransactionType.TronStakingTransactionType.Stake,
|
||||
is TransactionType.TronStakingTransactionType.Vote,
|
||||
is TransactionType.Staking.Stake,
|
||||
is TransactionType.Staking.Vote,
|
||||
is TransactionType.Staking.Restake,
|
||||
-> R.drawable.ic_transaction_history_staking_24
|
||||
is TransactionType.TronStakingTransactionType.ClaimRewards,
|
||||
is TransactionType.Staking.ClaimRewards,
|
||||
-> R.drawable.ic_transaction_history_claim_rewards_24
|
||||
is TransactionType.TronStakingTransactionType.Unstake,
|
||||
is TransactionType.TronStakingTransactionType.Withdraw,
|
||||
is TransactionType.Staking.Unstake,
|
||||
is TransactionType.Staking.Withdraw,
|
||||
-> R.drawable.ic_transaction_history_unstaking_24
|
||||
is TransactionType.Operation,
|
||||
is TransactionType.Swap,
|
||||
|
|
@ -70,11 +71,12 @@ internal class TxHistoryItemStateConverter(
|
|||
is TransactionType.Operation -> stringReference(type.name)
|
||||
is TransactionType.Swap -> resourceReference(R.string.common_swap)
|
||||
is TransactionType.Transfer -> resourceReference(R.string.common_transfer)
|
||||
is TransactionType.TronStakingTransactionType.Stake -> resourceReference(R.string.common_stake)
|
||||
is TransactionType.TronStakingTransactionType.Unstake -> resourceReference(R.string.common_unstake)
|
||||
is TransactionType.TronStakingTransactionType.Vote -> resourceReference(R.string.staking_vote)
|
||||
is TransactionType.TronStakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
|
||||
is TransactionType.TronStakingTransactionType.Withdraw -> { resourceReference(R.string.staking_withdraw) }
|
||||
is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake)
|
||||
is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake)
|
||||
is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote)
|
||||
is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
|
||||
is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw)
|
||||
is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake)
|
||||
is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation)
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +121,9 @@ internal class TxHistoryItemStateConverter(
|
|||
}
|
||||
|
||||
private fun TxHistoryItem.getAmount(): String {
|
||||
if (type is TransactionType.TronStakingTransactionType.Vote ||
|
||||
type == TransactionType.TronStakingTransactionType.ClaimRewards ||
|
||||
type == TransactionType.TronStakingTransactionType.Withdraw
|
||||
if (type is TransactionType.Staking.Vote ||
|
||||
type == TransactionType.Staking.ClaimRewards ||
|
||||
type == TransactionType.Staking.Withdraw
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,11 @@ private fun WalletContent(
|
|||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item(key = "WalletsList" + state.selectedWalletIndex, contentType = "WalletsList") {
|
||||
item(
|
||||
// !!! Type of the key should be saveable via Bundle on Android !!!
|
||||
key = state.wallets.map { it.walletCardState.id.stringValue },
|
||||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
) {
|
||||
WalletsList(
|
||||
lazyListState = walletsListState,
|
||||
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ zxingQrCode = "3.5.1"
|
|||
mviCore = "1.3.1"
|
||||
kotlinSerialization = "1.4.1"
|
||||
arrow = "1.2.3"
|
||||
walletConnectCore = "1.18.0"
|
||||
walletConnectWeb3 = "1.11.0"
|
||||
walletConnectCore = "1.35.2"
|
||||
walletConnectWeb3 = "1.35.2"
|
||||
prettyLogger = "2.2.0"
|
||||
okHttp-prettyLogging = "3.1.0"
|
||||
chucker = "4.0.0"
|
||||
|
|
@ -78,7 +78,6 @@ mlKit-barcodeScanning = "17.2.0"
|
|||
androidXCamera = "1.3.0"
|
||||
listenableFuture = "1.0"
|
||||
swipeRefreshLayout = "1.1.0"
|
||||
spr-client = "3.6.2"
|
||||
web3j = "4.10.1"
|
||||
leakcanary = "2.13"
|
||||
decompose = "2.2.2"
|
||||
|
|
@ -88,9 +87,9 @@ markdownComposeView = "0.5.4"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "develop-860"
|
||||
tangemBlockchainSdk = "develop-863"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-397"
|
||||
tangemCardSdk = "develop-400"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
tangemVico = "2.0.0-alpha.25-tangem17"
|
||||
#tangemVico = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
|
|
@ -416,4 +416,5 @@ private val excludedBlockchains = listOf(
|
|||
Blockchain.Unknown,
|
||||
Blockchain.Nexa,
|
||||
Blockchain.NexaTestnet,
|
||||
Blockchain.Xodex,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue