From c0471dbf9fc04833a0b45a6cdafebb7c0b370fd5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 Jul 2024 14:51:41 +0100 Subject: [PATCH 01/17] Updated on 2026-08-14 --- .../tap/proxy/TransactionManagerImpl.kt | 39 +-- .../models/response/ExchangeDataResponse.kt | 9 +- core/res/src/main/res/values-ru/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- core/res/src/main/res/values/strings.xml | 2 +- .../tokens/utils/CryptoCurrencyFactory.kt | 2 +- .../DefaultTransactionRepository.kt | 29 +-- .../transaction/TransactionRepository.kt | 1 - .../usecase/CreateTransactionUseCase.kt | 2 - .../swap/converters/ExpressDataConverter.kt | 12 + .../models/domain/ExpressTransactionModel.kt | 10 + .../swap/domain/models/ui/SwapState.kt | 9 + .../feature/swap/domain/SwapInteractorImpl.kt | 227 ++++++++++++++---- .../swap/models/SwapPermissionStateHolder.kt | 1 + .../feature/swap/models/SwapStateHolder.kt | 12 +- .../tangem/feature/swap/ui/StateBuilder.kt | 65 +++-- .../swap/ui/SwapPermissionBottomSheet.kt | 2 + .../feature/swap/ui/SwapScreenContent.kt | 2 +- .../tangem/feature/swap/ui/TransactionCard.kt | 13 +- .../tangem/lib/crypto/TransactionManager.kt | 7 +- 20 files changed, 315 insertions(+), 133 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index a18e07daef..8177d10df8 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -161,7 +161,7 @@ class TransactionManagerImpl( @Throws(IllegalStateException::class) override suspend fun getFee( networkId: String, - amountToSend: BigDecimal, + amountToSend: Amount, currencyToSend: Currency, destinationAddress: String, increaseBy: Int?, @@ -174,7 +174,7 @@ class TransactionManagerImpl( if (walletManager is EthereumOptimisticRollupWalletManager) { return getFeeForOptimismBlockchain( walletManager = walletManager, - amount = createAmount(amountToSend, currencyToSend, blockchain), + amount = amountToSend, destinationAddress = destinationAddress, data = data, ) @@ -183,7 +183,6 @@ class TransactionManagerImpl( walletManager = walletManager, blockchain = blockchain, amountToSend = amountToSend, - currency = currencyToSend, destinationAddress = destinationAddress, data = data, increaseBy = increaseBy, @@ -192,8 +191,6 @@ class TransactionManagerImpl( return getFeeForBlockchain( walletManager = walletManager, amountToSend = amountToSend, - currency = currencyToSend, - blockchain = blockchain, destinationAddress = destinationAddress, ) } @@ -209,13 +206,11 @@ class TransactionManagerImpl( private suspend fun getFeeForBlockchain( walletManager: WalletManager, - amountToSend: BigDecimal, - currency: Currency, - blockchain: Blockchain, + amountToSend: Amount, destinationAddress: String, ): ProxyFees { val fee = (walletManager as? TransactionSender)?.getFee( - amount = createAmount(amountToSend, currency, blockchain), + amount = amountToSend, destination = destinationAddress, ) ?: error("Cannot cast to TransactionSender") return when (fee) { @@ -268,17 +263,14 @@ class TransactionManagerImpl( private suspend fun getFeeForEthereumBlockchain( walletManager: EthereumWalletManager, blockchain: Blockchain, - amountToSend: BigDecimal, - currency: Currency, + amountToSend: Amount, destinationAddress: String, data: String?, increaseBy: Int?, ): ProxyFees { val gasLimit = getGasLimit( evmWalletManager = walletManager, - blockchain = blockchain, amount = amountToSend, - currency = currency, destinationAddress = destinationAddress, data = data, ).increaseBigIntegerByPercents(increaseBy) @@ -332,23 +324,20 @@ class TransactionManagerImpl( } } - @Suppress("LongParameterList") private suspend fun getGasLimit( evmWalletManager: EthereumWalletManager, - blockchain: Blockchain, - amount: BigDecimal, - currency: Currency, + amount: Amount, destinationAddress: String, data: String?, ): BigInteger { val result = if (data.isNullOrEmpty()) { evmWalletManager.getGasLimit( - amount = createAmount(amount, currency, blockchain), + amount = amount, destination = destinationAddress, ) } else { evmWalletManager.getGasLimit( - amount = createAmount(amount, currency, blockchain), + amount = amount, destination = destinationAddress, data = data, ) @@ -363,6 +352,18 @@ class TransactionManagerImpl( } } + override suspend fun getFeeForGas(networkId: String, gas: BigInteger, derivationPath: String?): ProxyFees { + val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val walletManager = getActualWalletManager(blockchain, derivationPath) + val gasPriceResult = (walletManager as? EthereumWalletManager)?.getGasPrice() + ?: error("not supported for $blockchain") + val gasPrice = when (gasPriceResult) { + is Result.Failure -> error("fail to receive gasPrice") + is Result.Success -> gasPriceResult.data + } + return createMultipleProxyFees(gasPrice, gas, blockchain) + } + private fun handleSendResult(result: Result): SendTxResult { when (result) { is Result.Success -> { diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeDataResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeDataResponse.kt index 7496a0a8e2..5e1eb6d5d3 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeDataResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeDataResponse.kt @@ -1,7 +1,6 @@ package com.tangem.datasource.api.express.models.response import com.squareup.moshi.Json -import java.math.BigDecimal data class ExchangeDataResponseWithTxDetails( val dataResponse: ExchangeDataResponse, @@ -50,7 +49,10 @@ data class TxDetails( val txData: String?, // transaction data if DEX, null if CEX @Json(name = "txValue") - val txValue: BigDecimal, // amount (same as fromAmount) + val txValue: String, // amount (same as fromAmount for Coin, but for bridge equal to otherNativeFee) + + @Json(name = "otherNativeFee") + val otherNativeFee: String?, @Json(name = "externalTxId") val externalTxId: String?, // null if DEX, provider transaction id if CEX @@ -63,6 +65,9 @@ data class TxDetails( @Json(name = "txExtraId") val txExtraId: String?, + + @Json(name = "gas") + val gas: String?, ) enum class TxType { diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index e70467d63f..3ee8a28d64 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -568,7 +568,7 @@ Дать разрешение Укажите лимит доступа к выбранному токену Количество %s - Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s + Чтобы продолжить, вам нужно разрешить смарт-контракту %1$s использовать ваш %2$s Безлимитно В процессе Обменять diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index e0b8c69381..2a44adee3c 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -283,7 +283,7 @@ 允許 賦予權限 數量 %s - 要繼續,您需要允許 1inch 智能合約使用您的 %s + 要繼續,您需要允許 %1$s 智能合約使用您的 %2$s 進行中 交易 選擇代幣 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 40b0b45e01..ffdb3de5f8 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -561,7 +561,7 @@ Give Permission Specify the approve limit for the selected token Amount %s - To continue, grant 1inch smart contracts permission to use your %s + To continue, grant %1$s smart contracts permission to use your %2$s Unlimited In progress Swap diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt index 55b2b0b161..b1e215ed19 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt @@ -99,7 +99,7 @@ class CryptoCurrencyFactory { decimals = cryptoCurrency.decimals, id = cryptoCurrency.id.rawCurrencyId, ) - val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.id.value) ?: Blockchain.Unknown + val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) ?: Blockchain.Unknown val id = getTokenId(network, sdkToken) return CryptoCurrency.Token( id = id, diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index d36cb4f74b..3f3d8aaa68 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -19,7 +19,6 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import timber.log.Timber -import java.math.BigDecimal internal class DefaultTransactionRepository( private val walletManagersFacade: WalletManagersFacade, @@ -34,7 +33,6 @@ internal class DefaultTransactionRepository( destination: String, userWalletId: UserWalletId, network: Network, - isSwap: Boolean, txExtras: TransactionExtras?, hash: String?, ): TransactionData? = withContext(coroutineDispatcherProvider.io) { @@ -51,7 +49,6 @@ internal class DefaultTransactionRepository( memo = memo, destination = destination, network = network, - isSwap = isSwap, txExtras = txExtras, hash = hash, ) @@ -84,7 +81,6 @@ internal class DefaultTransactionRepository( memo = memo, destination = destination, network = network, - isSwap = isSwap, txExtras = txExtras, hash = hash, ) @@ -118,23 +114,15 @@ internal class DefaultTransactionRepository( memo: String?, destination: String, network: Network, - isSwap: Boolean, txExtras: TransactionExtras?, hash: String?, ): TransactionData { - // TODO: refactor workaround to use general mechanism in bsdk for build tx for DEX - val txAmount = if (isSwap) { - createAmountForSwap(amount) - } else { - amount - } - if (txExtras != null && memo != null) { // throw error for now to avoid programmers errors when use extras error("Both txExtras and memo provided, use only one of them") } val extras = txExtras ?: getMemoExtras(network.id.value, memo) - return createTransaction(txAmount, fee, destination).copy( + return createTransaction(amount, fee, destination).copy( hash = hash, extras = extras, ) @@ -163,19 +151,4 @@ internal class DefaultTransactionRepository( else -> null } } - - private fun createAmountForSwap(amount: Amount): Amount { - return when (amount.type) { - is AmountType.Coin -> amount - else -> { - // 1. when creates swap amount for NonNativeToken, amount should be ZERO - // 2. Amount has .Coin type, as workaround to use destinationAddress in bsdk, not contractAddress - Amount( - currencySymbol = amount.currencySymbol, - value = BigDecimal.ZERO, - decimals = amount.decimals, - ) - } - } - } } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt index 52b707e8d1..181ce66767 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt @@ -16,7 +16,6 @@ interface TransactionRepository { destination: String, userWalletId: UserWalletId, network: Network, - isSwap: Boolean, txExtras: TransactionExtras?, hash: String?, ): TransactionData? diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/CreateTransactionUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/CreateTransactionUseCase.kt index 056dfc0f0c..ab5172eaf8 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/CreateTransactionUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/CreateTransactionUseCase.kt @@ -24,7 +24,6 @@ class CreateTransactionUseCase( userWalletId: UserWalletId, network: Network, txExtras: TransactionExtras? = null, - isSwap: Boolean = false, hash: String? = null, ) = Either.catch { requireNotNull( @@ -35,7 +34,6 @@ class CreateTransactionUseCase( destination = destination, userWalletId = userWalletId, network = network, - isSwap = isSwap, txExtras = txExtras, hash = hash, ), diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt index 1fb2d9a8d2..34c663840b 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt @@ -8,6 +8,7 @@ import com.tangem.feature.swap.domain.models.createFromAmountWithOffset import com.tangem.feature.swap.domain.models.domain.ExpressTransactionModel import com.tangem.feature.swap.domain.models.domain.SwapDataModel import com.tangem.utils.converter.Converter +import java.math.BigDecimal internal class ExpressDataConverter : Converter { @@ -24,19 +25,30 @@ internal class ExpressDataConverter : Converter = emptyList(), + val swapProvider: SwapProvider, ) : SwapState data class EmptyAmountState(val zeroAmountEquivalent: String) : SwapState @@ -102,6 +108,9 @@ data class TxFee( val gasLimit: Int, val feeFiatFormatted: String, val feeCryptoFormatted: String, + val feeIncludeOtherNativeFee: BigDecimal, + val feeFiatFormattedWithNative: String, + val feeCryptoFormattedWithNative: String, val decimals: Int, val cryptoSymbol: String, val feeType: FeeType, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index df53e5e0e7..e5111d0545 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -4,8 +4,11 @@ import arrow.core.Either import arrow.core.getOrElse import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.minimalAmount import com.tangem.common.extensions.hexToBytes import com.tangem.core.ui.utils.BigDecimalFormatter @@ -15,10 +18,8 @@ import com.tangem.domain.appcurrency.extenstions.unwrap import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.model.FeePaidCurrency -import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.QuotesRepository @@ -342,7 +343,7 @@ internal class SwapInteractorImpl @Inject constructor( ) } else { provider to getQuotesState( - exchangeProviderType = provider.type, + provider = provider, quoteDataModel = quotes, amount = amount, fromToken = fromToken, @@ -366,7 +367,6 @@ internal class SwapInteractorImpl @Inject constructor( isBalanceWithoutFeeEnough: Boolean, ): Pair { return provider to loadCexQuoteData( - exchangeProviderType = ExchangeProviderType.CEX, networkId = networkId, amount = amount, fromTokenStatus = fromToken, @@ -595,8 +595,8 @@ internal class SwapInteractorImpl @Inject constructor( swapData = requireNotNull(swapData), currencyToSendStatus = currencyToSend, currencyToGetStatus = currencyToGet, - amountToSwap = amountToSwap, fee = fee, + amountToSwap = amountToSwap, userWalletId = requireNotNull(getSelectedWallet()).walletId, ) } @@ -622,8 +622,8 @@ internal class SwapInteractorImpl @Inject constructor( ) val fee = when (val txFee = state.txFee) { TxFeeState.Empty -> BigDecimal.ZERO - is TxFeeState.MultipleFeeState -> txFee.priorityFee.feeValue - is TxFeeState.SingleFeeState -> txFee.fee.feeValue + is TxFeeState.MultipleFeeState -> txFee.priorityFee.feeIncludeOtherNativeFee + is TxFeeState.SingleFeeState -> txFee.fee.feeIncludeOtherNativeFee } val feeState = getFeeState( fee = fee, @@ -656,8 +656,9 @@ internal class SwapInteractorImpl @Inject constructor( val derivationPath = currencyToSendStatus.currency.network.derivationPath.value val dexTransaction = swapData.transaction as ExpressTransactionModel.DEX val dataToSign = dexTransaction.txData + val amountToSend = createNativeAmountForDex(swapData.transaction.txValue, currencyToSendStatus.currency.network) val txData = createTransactionUseCase( - amount = amount.value.convertToAmount(currencyToSendStatus.currency), + amount = amountToSend, fee = getFeeForTransaction( fee = fee, blockchain = Blockchain.fromId(currencyToSendStatus.currency.network.id.value), @@ -668,7 +669,6 @@ internal class SwapInteractorImpl @Inject constructor( network = currencyToSendStatus.currency.network, txExtras = createDexTxExtras(fee.gasLimit, dataToSign), hash = dataToSign, - isSwap = true, ).getOrElse { Timber.e(it) return SwapTransactionState.UnknownError @@ -984,7 +984,6 @@ internal class SwapInteractorImpl @Inject constructor( */ @Suppress("LongParameterList") private suspend fun loadCexQuoteData( - exchangeProviderType: ExchangeProviderType, networkId: String, amount: SwapAmount, fromTokenStatus: CryptoCurrencyStatus, @@ -1035,7 +1034,7 @@ internal class SwapInteractorImpl @Inject constructor( ) getQuotesState( - exchangeProviderType = exchangeProviderType, + provider = provider, quoteDataModel = quotes, amount = amount, fromToken = fromTokenStatus, @@ -1052,7 +1051,7 @@ internal class SwapInteractorImpl @Inject constructor( @Suppress("LongMethod") private suspend fun getQuotesState( - exchangeProviderType: ExchangeProviderType, + provider: SwapProvider, quoteDataModel: Either, amount: SwapAmount, fromToken: CryptoCurrencyStatus, @@ -1074,6 +1073,7 @@ internal class SwapInteractorImpl @Inject constructor( toTokenAmount = quoteModel.toTokenAmount, swapData = null, txFeeState = txFee, + provider = provider, ).copy( warnings = manageWarnings( fromTokenStatus = fromToken, @@ -1083,7 +1083,7 @@ internal class SwapInteractorImpl @Inject constructor( ), ) - when (exchangeProviderType) { + when (provider.type) { ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> { val state = updatePermissionState( networkId = networkId, @@ -1147,8 +1147,8 @@ internal class SwapInteractorImpl @Inject constructor( ): IncludeFeeInAmount { val feeValue = when (txFee) { TxFeeState.Empty -> BigDecimal.ZERO - is TxFeeState.MultipleFeeState -> txFee.priorityFee.feeValue - is TxFeeState.SingleFeeState -> txFee.fee.feeValue + is TxFeeState.MultipleFeeState -> txFee.priorityFee.feeIncludeOtherNativeFee + is TxFeeState.SingleFeeState -> txFee.fee.feeIncludeOtherNativeFee } val feePaidCurrency = getFeePaidCurrency( userWalletId = requireNotNull(getSelectedWallet()).walletId, @@ -1254,29 +1254,35 @@ internal class SwapInteractorImpl @Inject constructor( providerId = provider.providerId, rateType = RateType.FLOAT, toAddress = toToken.value.networkAddress?.defaultAddress?.value.orEmpty(), + refundAddress = fromToken.value.networkAddress?.defaultAddress?.value, ).fold( ifRight = { swapData -> - val feeData = transactionManager.getFee( - networkId = networkId, - amountToSend = amount.value, - currencyToSend = swapCurrencyConverter.convert(fromToken.currency), - destinationAddress = swapData.transaction.txTo, - increaseBy = INCREASE_GAS_LIMIT_BY, - data = (swapData.transaction as ExpressTransactionModel.DEX).txData, - derivationPath = fromToken.currency.network.derivationPath.value, - ) - val txFeeState = when (feeData) { - is ProxyFees.MultipleFees -> feeData.proxyFeesToFeeState(fromToken.currency) - is ProxyFees.SingleFee -> feeData.proxyFeesToFeeState(fromToken.currency) + val transaction = swapData.transaction as ExpressTransactionModel.DEX + val nativeCoinDecimals = Blockchain.fromNetworkId(networkId)?.decimals() + ?: error("Blockchain not found") + val otherNativeFee = transaction.otherNativeFeeWei + ?.movePointLeft(nativeCoinDecimals) + ?: BigDecimal.ZERO + val txFeeState = when (val feeData = getFeeDataForDexSwap(networkId, transaction, fromToken.currency)) { + is ProxyFees.MultipleFees -> feeData.proxyFeesToFeeState(fromToken.currency, otherNativeFee) + is ProxyFees.SingleFee -> feeData.proxyFeesToFeeState(fromToken.currency, otherNativeFee) } val feeByPriority = selectFeeByType(feeType = selectedFee, txFeeState = txFeeState) - val isBalanceIncludeFeeEnough = isBalanceEnough(fromToken, amount, feeByPriority) + val feeToCheckFunds = feeByPriority + (otherNativeFee ?: BigDecimal.ZERO) + val isBalanceIncludeFeeEnough = isBalanceEnough(fromToken, amount, feeToCheckFunds) val feeState = getFeeState( fee = feeByPriority, spendAmount = amount, networkId = networkId, fromTokenStatus = fromToken, ) + val preparedSwapConfigState = PreparedSwapConfigState( + isAllowedToSpend = true, + isBalanceEnough = isBalanceIncludeFeeEnough, + feeState = feeState, + hasOutgoingTransaction = hasOutgoingTransaction(fromToken), + includeFeeInAmount = IncludeFeeInAmount.Excluded, // exclude for dex + ) val swapState = updateBalances( networkId = networkId, fromTokenStatus = fromToken, @@ -1285,6 +1291,7 @@ internal class SwapInteractorImpl @Inject constructor( toTokenAmount = swapData.toTokenAmount, swapData = swapData, txFeeState = txFeeState, + provider = provider, ) swapState.copy( permissionState = PermissionDataState.Empty, @@ -1292,17 +1299,9 @@ internal class SwapInteractorImpl @Inject constructor( fromTokenStatus = fromToken, amount = amount, feeState = txFeeState, - minAdaValue = (feeData as? ProxyFees.SingleFee)?.let { - (it.singleFee as? ProxyFee.CardanoToken)?.minAdaValue - }, - ), - preparedSwapConfigState = PreparedSwapConfigState( - isAllowedToSpend = true, - isBalanceEnough = isBalanceIncludeFeeEnough, - feeState = feeState, - hasOutgoingTransaction = hasOutgoingTransaction(fromToken), - includeFeeInAmount = IncludeFeeInAmount.Excluded, // exclude for dex + minAdaValue = null, // no ADA in DEX ), + preparedSwapConfigState = preparedSwapConfigState, ) }, ifLeft = { error -> @@ -1322,8 +1321,42 @@ internal class SwapInteractorImpl @Inject constructor( ) } + private suspend fun getFeeDataForDexSwap( + networkId: String, + transaction: ExpressTransactionModel.DEX, + fromToken: CryptoCurrency, + ): ProxyFees { + return try { + val nativeBalance = userWalletManager.getNativeTokenBalance( + networkId = networkId, + derivationPath = fromToken.network.derivationPath.value, + ) ?: ProxyAmount.empty() + val amountToSend = createNativeAmountForDex(transaction.txValue, fromToken.network) + // transaction.txValue is always native coin + if (nativeBalance.value < amountToSend.value) { + error("It's impossible to calculate fee for nativeBalance.value < amountToSend.value") + } + transactionManager.getFee( + networkId = networkId, + amountToSend = amountToSend, + currencyToSend = swapCurrencyConverter.convert(fromToken), + destinationAddress = transaction.txTo, + increaseBy = INCREASE_GAS_LIMIT_BY, + data = transaction.txData, + derivationPath = fromToken.network.derivationPath.value, + ) + } catch (e: IllegalStateException) { + transactionManager.getFeeForGas( + networkId = networkId, + gas = transaction.gas, + derivationPath = fromToken.network.derivationPath.value, + ) + } + } + @Suppress("LongParameterList") private suspend fun updateBalances( + provider: SwapProvider, networkId: String, fromTokenStatus: CryptoCurrencyStatus, toTokenStatus: CryptoCurrencyStatus, @@ -1335,7 +1368,6 @@ internal class SwapInteractorImpl @Inject constructor( val fromToken = fromTokenStatus.currency val toToken = toTokenStatus.currency val nativeToken = repository.getNativeTokenForNetwork(networkId) - val rates = getQuotes(fromToken.id, toToken.id, nativeToken.id) return SwapState.QuotesLoadedState( fromTokenInfo = TokenSwapInfo( @@ -1358,6 +1390,7 @@ internal class SwapInteractorImpl @Inject constructor( ), swapDataModel = swapData, txFee = txFeeState, + swapProvider = provider, ) } @@ -1367,7 +1400,7 @@ internal class SwapInteractorImpl @Inject constructor( ): TxFeeState { return txFeeResult?.fold( ifLeft = { TxFeeState.Empty }, - ifRight = { txFee -> txFee.toTxFeeState(fromToken.currency) }, + ifRight = { txFee -> txFee.toTxFeeState(fromToken.currency, null) }, ) ?: TxFeeState.Empty } @@ -1427,7 +1460,7 @@ internal class SwapInteractorImpl @Inject constructor( try { transactionManager.getFee( networkId = networkId, - amountToSend = BigDecimal.ZERO, + amountToSend = createNativeAmountForDex("0", fromToken.network), currencyToSend = swapCurrencyConverter.convert(repository.getNativeTokenForNetwork(networkId)), destinationAddress = fromToken.getContractAddress(), increaseBy = INCREASE_GAS_LIMIT_BY, @@ -1475,11 +1508,16 @@ internal class SwapInteractorImpl @Inject constructor( ) } - private suspend fun ProxyFees.MultipleFees.proxyFeesToFeeState(fromToken: CryptoCurrency): TxFeeState { + private suspend fun ProxyFees.MultipleFees.proxyFeesToFeeState( + fromToken: CryptoCurrency, + otherNativeFee: BigDecimal? = null, + ): TxFeeState { + val otherNativeFeeValue = otherNativeFee ?: BigDecimal.ZERO val normalFeeValue = this.minFee.fee.value // in swap for normal use min fee val normalFeeGas = this.minFee.gasLimit.toInt() val priorityFeeValue = this.normalFee.fee.value // in swap for priority use normal fee val priorityFeeGas = this.normalFee.gasLimit.toInt() + // region fees to use val feesFiat = getFormattedFiatFees(fromToken, normalFeeValue, priorityFeeValue) val normalFiatFee = requireNotNull(feesFiat.getOrNull(0)) { "feesFiat item 0 couldn't be null" } val priorityFiatFee = requireNotNull(feesFiat.getOrNull(1)) { "feesFiat item 1 couldn't be null" } @@ -1491,12 +1529,35 @@ internal class SwapInteractorImpl @Inject constructor( amount = priorityFeeValue, decimals = normalFee.fee.decimals, ) + // + // region fees include otherNativeFee + val feesFiatWithNative = getFormattedFiatFees( + fromToken = fromToken, + normalFeeValue + otherNativeFeeValue, + priorityFeeValue + otherNativeFeeValue, + ) + val normalFiatFeeWithNative = + requireNotNull(feesFiatWithNative.getOrNull(0)) { "feesFiat item 0 couldn't be null" } + val priorityFiatFeeWithNative = + requireNotNull(feesFiatWithNative.getOrNull(1)) { "feesFiat item 1 couldn't be null" } + val normalCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = normalFeeValue + otherNativeFeeValue, + decimals = minFee.fee.decimals, + ) + val priorityCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = priorityFeeValue + otherNativeFeeValue, + decimals = normalFee.fee.decimals, + ) + // return TxFeeState.MultipleFeeState( normalFee = TxFee( feeValue = normalFeeValue, gasLimit = normalFeeGas, feeFiatFormatted = normalFiatFee, feeCryptoFormatted = normalCryptoFee, + feeIncludeOtherNativeFee = normalFeeValue + otherNativeFeeValue, + feeFiatFormattedWithNative = normalFiatFeeWithNative, + feeCryptoFormattedWithNative = normalCryptoFeeWithNative, decimals = minFee.fee.decimals, cryptoSymbol = minFee.fee.currencySymbol, feeType = FeeType.NORMAL, @@ -1506,6 +1567,9 @@ internal class SwapInteractorImpl @Inject constructor( gasLimit = priorityFeeGas, feeFiatFormatted = priorityFiatFee, feeCryptoFormatted = priorityCryptoFee, + feeIncludeOtherNativeFee = priorityFeeValue + otherNativeFeeValue, + feeFiatFormattedWithNative = priorityFiatFeeWithNative, + feeCryptoFormattedWithNative = priorityCryptoFeeWithNative, decimals = normalFee.fee.decimals, cryptoSymbol = normalFee.fee.currencySymbol, feeType = FeeType.PRIORITY, @@ -1513,7 +1577,11 @@ internal class SwapInteractorImpl @Inject constructor( ) } - private suspend fun ProxyFees.SingleFee.proxyFeesToFeeState(fromToken: CryptoCurrency): TxFeeState { + private suspend fun ProxyFees.SingleFee.proxyFeesToFeeState( + fromToken: CryptoCurrency, + otherNativeFee: BigDecimal? = null, + ): TxFeeState { + val otherNativeFeeValue = otherNativeFee ?: BigDecimal.ZERO val normalFeeValue = this.singleFee.fee.value val normalFeeGas = this.singleFee.gasLimit.toInt() val feesFiat = getFormattedFiatFees(fromToken, normalFeeValue) @@ -1522,12 +1590,28 @@ internal class SwapInteractorImpl @Inject constructor( amount = normalFeeValue, decimals = singleFee.fee.decimals, ) + // region fees include otherNativeFee + val feesFiatWithNative = getFormattedFiatFees( + fromToken = fromToken, + normalFeeValue + otherNativeFeeValue, + normalFeeValue + otherNativeFeeValue, + ) + val normalFiatFeeWithNative = + requireNotNull(feesFiatWithNative.getOrNull(0)) { "feesFiat item 0 couldn't be null" } + val normalCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = normalFeeValue + otherNativeFeeValue, + decimals = singleFee.fee.decimals, + ) + // return TxFeeState.SingleFeeState( fee = TxFee( feeValue = normalFeeValue, gasLimit = normalFeeGas, feeFiatFormatted = normalFiatFee, feeCryptoFormatted = normalCryptoFee, + feeIncludeOtherNativeFee = normalFeeValue + otherNativeFeeValue, + feeFiatFormattedWithNative = normalFiatFeeWithNative, + feeCryptoFormattedWithNative = normalCryptoFeeWithNative, decimals = singleFee.fee.decimals, cryptoSymbol = singleFee.fee.currencySymbol, feeType = FeeType.NORMAL, @@ -1535,7 +1619,12 @@ internal class SwapInteractorImpl @Inject constructor( ) } - private suspend fun TransactionFee.toTxFeeState(fromToken: CryptoCurrency): TxFeeState { + @Suppress("LongMethod") + private suspend fun TransactionFee.toTxFeeState( + fromToken: CryptoCurrency, + otherNativeFee: BigDecimal?, + ): TxFeeState { + val otherNativeFeeValue = otherNativeFee ?: BigDecimal.ZERO return when (this) { is TransactionFee.Choosable -> { val normalFee = this.normal.increaseGasLimitBy(INCREASE_GAS_LIMIT_FOR_SEND) @@ -1553,12 +1642,31 @@ internal class SwapInteractorImpl @Inject constructor( amount = feePriority, decimals = priorityFee.amount.decimals, ) + + // region otherNativeFee + val normalFeeWithOtherNative = feeNormal + otherNativeFeeValue + val priorityFeeWithOtherNative = feeNormal + otherNativeFeeValue + val normalFiatValueWithNative = getFormattedFiatFees(fromToken, normalFeeWithOtherNative)[0] + val priorityFiatValueWithNative = getFormattedFiatFees(fromToken, priorityFeeWithOtherNative)[0] + + val normalCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = normalFeeWithOtherNative, + decimals = normalFee.amount.decimals, + ) + val priorityCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = priorityFeeWithOtherNative, + decimals = priorityFee.amount.decimals, + ) + // TxFeeState.MultipleFeeState( normalFee = TxFee( feeValue = feeNormal, gasLimit = normalFee.getGasLimit(), feeFiatFormatted = normalFiatValue, feeCryptoFormatted = normalCryptoFee, + feeIncludeOtherNativeFee = normalFeeWithOtherNative, + feeFiatFormattedWithNative = normalFiatValueWithNative, + feeCryptoFormattedWithNative = normalCryptoFeeWithNative, decimals = normalFee.amount.decimals, cryptoSymbol = normalFee.amount.currencySymbol, feeType = FeeType.NORMAL, @@ -1568,6 +1676,9 @@ internal class SwapInteractorImpl @Inject constructor( gasLimit = priorityFee.getGasLimit(), feeFiatFormatted = priorityFiatValue, feeCryptoFormatted = priorityCryptoFee, + feeIncludeOtherNativeFee = priorityFeeWithOtherNative, + feeFiatFormattedWithNative = priorityFiatValueWithNative, + feeCryptoFormattedWithNative = priorityCryptoFeeWithNative, decimals = priorityFee.amount.decimals, cryptoSymbol = priorityFee.amount.currencySymbol, feeType = FeeType.PRIORITY, @@ -1581,12 +1692,24 @@ internal class SwapInteractorImpl @Inject constructor( amount = feeNormal, decimals = this.normal.amount.decimals, ) + // region otherNativeFee + val normalFeeWithOtherNative = feeNormal + otherNativeFeeValue + val normalFiatValueWithNative = getFormattedFiatFees(fromToken, normalFeeWithOtherNative)[0] + + val normalCryptoFeeWithNative = amountFormatter.formatBigDecimalAmountToUI( + amount = normalFeeWithOtherNative, + decimals = this.normal.amount.decimals, + ) + // TxFeeState.SingleFeeState( fee = TxFee( feeValue = this.normal.amount.value ?: BigDecimal.ZERO, gasLimit = this.normal.getGasLimit(), feeFiatFormatted = normalFiatValue, feeCryptoFormatted = normalCryptoFee, + feeIncludeOtherNativeFee = normalFeeWithOtherNative, + feeFiatFormattedWithNative = normalFiatValueWithNative, + feeCryptoFormattedWithNative = normalCryptoFeeWithNative, decimals = normal.amount.decimals, cryptoSymbol = normal.amount.currencySymbol, feeType = FeeType.NORMAL, @@ -1596,6 +1719,18 @@ internal class SwapInteractorImpl @Inject constructor( } } + private fun createNativeAmountForDex(txValueAmount: String, network: Network): Amount { + val nativeDecimals = Blockchain.fromNetworkId(network.backendId)?.decimals() + ?: error("Blockchain not found") + val decimalValue = txValueAmount.toBigDecimalOrNull()?.movePointLeft(nativeDecimals) + ?: error("txValue parse error") + return Amount( + currencySymbol = network.currencySymbol, + value = decimalValue, + decimals = nativeDecimals, + ) + } + /** * Workaround to increase gas limit cause we calculate fee for random address */ @@ -1656,7 +1791,7 @@ internal class SwapInteractorImpl @Inject constructor( if (fromToken.currency is CryptoCurrency.Token) { tokenBalance >= amount.value } else { - tokenBalance > amount.value.plus(fee ?: BigDecimal.ZERO) + tokenBalance >= amount.value.plus(fee ?: BigDecimal.ZERO) } } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapPermissionStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapPermissionStateHolder.kt index 4192106336..7e31eb9e60 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapPermissionStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapPermissionStateHolder.kt @@ -12,6 +12,7 @@ sealed class SwapPermissionState { object Empty : SwapPermissionState() data class ReadyForRequest( + val providerName: String, val currency: String, val amount: String, val walletAddress: String, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index cd22de2a3b..6e429c9c13 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -1,7 +1,6 @@ package com.tangem.feature.swap.models import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.ui.text.input.TextFieldValue import com.tangem.core.ui.R import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig @@ -74,18 +73,21 @@ data class SwapButton( sealed interface TransactionCardType { - val headerResId: Int + val header: TextReference + val isError: Boolean data class Inputtable( val onAmountChanged: ((String) -> Unit), val onFocusChanged: ((Boolean) -> Unit), - @StringRes override val headerResId: Int = R.string.swapping_from_title, + override val isError: Boolean, + override val header: TextReference = TextReference.Res(R.string.swapping_from_title), ) : TransactionCardType data class ReadOnly( val showWarning: Boolean = false, val onWarningClick: (() -> Unit)? = null, - @StringRes override val headerResId: Int = R.string.swapping_to_title, + override val isError: Boolean = false, + override val header: TextReference = TextReference.Res(R.string.swapping_to_title), ) : TransactionCardType } @@ -102,7 +104,7 @@ data class LegalState( sealed interface SwapWarning { data class PermissionNeeded(val notificationConfig: NotificationConfig) : SwapWarning - object InsufficientFunds : SwapWarning + data object InsufficientFunds : SwapWarning data class NoAvailableTokensToSwap(val notificationConfig: NotificationConfig) : SwapWarning data class GenericWarning( val title: TextReference? = null, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 1925e60ee7..05a1fa9451 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -51,7 +51,11 @@ internal class StateBuilder( return SwapStateHolder( blockchainId = networkInfo.blockchainId, sendCardData = SwapCardState.SwapCardData( - type = TransactionCardType.Inputtable(actions.onAmountChanged, actions.onAmountSelected), + type = TransactionCardType.Inputtable( + onAmountChanged = actions.onAmountChanged, + onFocusChanged = actions.onAmountSelected, + isError = false, + ), amountEquivalent = null, amountTextFieldValue = null, token = null, @@ -152,9 +156,13 @@ 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), + ) return uiStateHolder.copy( sendCardData = SwapCardState.SwapCardData( - type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable), + type = sendInput, amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue, amountEquivalent = null, token = uiStateHolder.sendCardData.token, @@ -221,9 +229,19 @@ internal class StateBuilder( val feeState = createFeeState(quoteModel.txFee, selectedFeeType) val fromCurrencyStatus = quoteModel.fromTokenInfo.cryptoCurrencyStatus val toCurrencyStatus = quoteModel.toTokenInfo.cryptoCurrencyStatus + val isInsufficientFunds = isInsufficientFundsCondition(quoteModel) + val insufficientFundsHeader = if (isInsufficientFunds) { + TextReference.Res(R.string.swapping_insufficient_funds) + } else { + TextReference.Res(R.string.swapping_from_title) + } + val sendInput = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable).copy( + isError = isInsufficientFunds, + header = insufficientFundsHeader, + ) return uiStateHolder.copy( sendCardData = SwapCardState.SwapCardData( - type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable), + type = sendInput, amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue, amountEquivalent = getFormattedFiatAmount(quoteModel.fromTokenInfo.amountFiat), token = fromCurrencyStatus, @@ -257,6 +275,7 @@ internal class StateBuilder( permissionState = convertPermissionState( lastPermissionState = uiStateHolder.permissionState, permissionDataState = quoteModel.permissionState, + providerName = quoteModel.swapProvider.name, onGivePermissionClick = actions.onGivePermissionClick, onChangeApproveType = actions.onChangeApproveType, ), @@ -320,7 +339,7 @@ internal class StateBuilder( val warnings = mutableListOf() maybeAddDomainWarnings(quoteModel, warnings) maybeAddNeedReserveToCreateAccountWarning(quoteModel, warnings) - maybeAddPermissionNeededWarning(quoteModel, warnings, fromToken) + maybeAddPermissionNeededWarning(quoteModel, warnings, fromToken, quoteModel.swapProvider.name) maybeAddNetworkFeeCoverageWarning(quoteModel, warnings, selectedFeeType) maybeAddUnableCoverFeeWarning(quoteModel, fromToken, warnings) maybeAddInsufficientFundsWarning(quoteModel, warnings) @@ -468,6 +487,7 @@ internal class StateBuilder( quoteModel: SwapState.QuotesLoadedState, warnings: MutableList, fromToken: CryptoCurrency, + providerName: String, ) { if (!quoteModel.preparedSwapConfigState.isAllowedToSpend && quoteModel.preparedSwapConfigState.feeState is SwapFeeState.Enough && @@ -475,7 +495,7 @@ internal class StateBuilder( ) { warnings.add( SwapWarning.PermissionNeeded( - createPermissionNotificationConfig(fromToken.symbol), + createPermissionNotificationConfig(fromToken.symbol, providerName), ), ) } @@ -493,8 +513,8 @@ internal class StateBuilder( warnings.add( SwapWarning.GeneralWarning( createNetworkFeeCoverageNotificationConfig( - fee.feeCryptoFormatted, - fee.feeFiatFormatted, + fee.feeCryptoFormattedWithNative, + fee.feeFiatFormattedWithNative, ), ), ) @@ -547,13 +567,16 @@ internal class StateBuilder( warnings: MutableList, ) { // check isBalanceEnough, but for dex includeFeeInAmount always Excluded - if (!quoteModel.preparedSwapConfigState.isBalanceEnough && - quoteModel.preparedSwapConfigState.includeFeeInAmount !is IncludeFeeInAmount.Included - ) { + if (isInsufficientFundsCondition(quoteModel)) { warnings.add(SwapWarning.InsufficientFunds) } } + private fun isInsufficientFundsCondition(quoteModel: SwapState.QuotesLoadedState): Boolean { + return !quoteModel.preparedSwapConfigState.isBalanceEnough && + quoteModel.preparedSwapConfigState.includeFeeInAmount !is IncludeFeeInAmount.Included + } + private fun getSwapButtonEnabled(quoteModel: SwapState.QuotesLoadedState): Boolean { val status = quoteModel.toTokenInfo.cryptoCurrencyStatus.value if (status is CryptoCurrencyStatus.NoAccount) { @@ -964,9 +987,9 @@ internal class StateBuilder( return FeeItemState.Content( feeType = feeType, title = resourceReference(R.string.common_network_fee_title), - amountCrypto = fee.feeCryptoFormatted, + amountCrypto = fee.feeCryptoFormattedWithNative, // display fee with native as workaround for okx symbolCrypto = fee.cryptoSymbol, - amountFiatFormatted = fee.feeFiatFormatted, + amountFiatFormatted = fee.feeFiatFormattedWithNative, // display fee with native as workaround for okx isClickable = isClickable, onClick = actions.onClickFee, ) @@ -1017,7 +1040,7 @@ internal class StateBuilder( showStatusButton = shouldShowStatus, providerIcon = providerState.iconUrl, rate = providerState.subtitle, - fee = stringReference("${fee.feeCryptoFormatted} (${fee.feeFiatFormatted})"), + fee = stringReference("${fee.feeCryptoFormattedWithNative} (${fee.feeFiatFormattedWithNative})"), fromTokenAmount = stringReference(swapTransactionState.fromAmount.orEmpty()), toTokenAmount = stringReference(swapTransactionState.toAmount.orEmpty()), fromTokenFiatAmount = stringReference(fromFiatAmount), @@ -1146,6 +1169,7 @@ internal class StateBuilder( private fun convertPermissionState( lastPermissionState: SwapPermissionState, permissionDataState: PermissionDataState, + providerName: String, onGivePermissionClick: () -> Unit, onChangeApproveType: (ApproveType) -> Unit, ): SwapPermissionState { @@ -1165,6 +1189,7 @@ internal class StateBuilder( is TxFeeState.SingleFeeState -> fee.fee } SwapPermissionState.ReadyForRequest( + providerName = providerName, currency = permissionDataState.currency, amount = permissionDataState.amount, approveType = approveType, @@ -1358,18 +1383,18 @@ internal class StateBuilder( FeeItemState.Content( feeType = this.normalFee.feeType, title = resourceReference(R.string.common_network_fee_title), - amountCrypto = this.normalFee.feeCryptoFormatted, + amountCrypto = this.normalFee.feeCryptoFormattedWithNative, symbolCrypto = this.normalFee.cryptoSymbol, - amountFiatFormatted = this.normalFee.feeFiatFormatted, + amountFiatFormatted = this.normalFee.feeFiatFormattedWithNative, isClickable = true, onClick = {}, ), FeeItemState.Content( feeType = this.priorityFee.feeType, title = resourceReference(R.string.common_network_fee_title), - amountCrypto = this.priorityFee.feeCryptoFormatted, + amountCrypto = this.priorityFee.feeCryptoFormattedWithNative, symbolCrypto = this.priorityFee.cryptoSymbol, - amountFiatFormatted = this.priorityFee.feeFiatFormatted, + amountFiatFormatted = this.priorityFee.feeFiatFormattedWithNative, isClickable = true, onClick = {}, ), @@ -1402,12 +1427,12 @@ internal class StateBuilder( } // region warnings - private fun createPermissionNotificationConfig(fromTokenSymbol: String): NotificationConfig { + private fun createPermissionNotificationConfig(fromTokenSymbol: String, providerName: String): NotificationConfig { return NotificationConfig( title = resourceReference(R.string.express_provider_permission_needed), subtitle = resourceReference( id = R.string.swapping_permission_subheader, - formatArgs = wrappedList(fromTokenSymbol), + formatArgs = wrappedList(providerName, fromTokenSymbol), ), iconResId = R.drawable.ic_locked_24, ) @@ -1604,7 +1629,7 @@ internal class StateBuilder( id = this.providerId, name = this.name, iconUrl = this.imageLarge, - type = this.type.toString(), + type = this.type.providerName, selectionType = selectionType, alertText = alertText, onProviderClick = onProviderClick, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt index 8ac175588a..1915863d98 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt @@ -68,6 +68,7 @@ private fun SwapPermissionBottomSheetContent(content: GivePermissionBottomSheetC Text( text = stringResource( id = R.string.swapping_permission_subheader, + data.providerName, data.currency, ), color = TangemTheme.colors.text.secondary, @@ -303,6 +304,7 @@ private fun Preview_AgreementBottomSheet() { private val previewData = GivePermissionBottomSheetConfig( data = SwapPermissionState.ReadyForRequest( + providerName = "1icnh", currency = "DAI", amount = "∞", walletAddress = "", diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index 7be20ace87..fc0b98feb3 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -434,7 +434,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U // region preview private val sendCard = SwapCardState.SwapCardData( - type = TransactionCardType.Inputtable({}, {}), + type = TransactionCardType.Inputtable({}, {}, false), amountTextFieldValue = TextFieldValue(), amountEquivalent = "1 000 000", tokenIconUrl = "", diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index 7ae3edc6a2..9baf09e90a 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -37,6 +37,7 @@ import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import com.tangem.core.ui.R import com.tangem.core.ui.components.* +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.utils.ImageBackgroundContrastChecker @@ -185,10 +186,14 @@ private fun Header(type: TransactionCardType, balance: String, modifier: Modifie horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - val title = type.headerResId + val titleColor = if (type.isError) { + TangemTheme.colors.text.warning + } else { + TangemTheme.colors.text.tertiary + } Text( - text = stringResource(id = title), - color = TangemTheme.colors.text.tertiary, + text = type.header.resolveReference(), + color = titleColor, maxLines = 1, style = MaterialTheme.typography.subtitle2, modifier = Modifier @@ -546,7 +551,7 @@ private fun Preview_TransactionCardWithoutPriceImpact_InDarkTheme() { @Composable private fun TransactionCardPreview() { TransactionCard( - type = TransactionCardType.Inputtable({}, {}), + type = TransactionCardType.Inputtable({}, {}, false), amountEquivalent = "1 000 000", tokenIconUrl = "", tokenCurrency = "DAI", diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt index 6b69d96fb8..a3a9846b43 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt @@ -1,9 +1,11 @@ package com.tangem.lib.crypto +import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionExtras import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import java.math.BigDecimal +import java.math.BigInteger interface TransactionManager { @@ -46,7 +48,7 @@ interface TransactionManager { @Throws(IllegalStateException::class) suspend fun getFee( networkId: String, - amountToSend: BigDecimal, + amountToSend: Amount, currencyToSend: Currency, destinationAddress: String, increaseBy: Int?, @@ -54,6 +56,9 @@ interface TransactionManager { derivationPath: String?, ): ProxyFees + @Throws(IllegalStateException::class) + suspend fun getFeeForGas(networkId: String, gas: BigInteger, derivationPath: String?): ProxyFees + @Throws(IllegalStateException::class) suspend fun updateWalletManager(networkId: String, derivationPath: String?) From 4e21f84b901d902b234a60159d9146fb3a333794 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 9 Jul 2024 12:57:28 +0100 Subject: [PATCH 02/17] Updated on 2026-08-14 --- .../java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 4 ++-- .../src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index e5111d0545..25d18c1f45 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1348,7 +1348,7 @@ internal class SwapInteractorImpl @Inject constructor( } catch (e: IllegalStateException) { transactionManager.getFeeForGas( networkId = networkId, - gas = transaction.gas, + gas = transaction.gas.multiply(INCREASE_GAS_LIMIT_BY.toBigInteger()).divide(100.toBigInteger()), derivationPath = fromToken.network.derivationPath.value, ) } @@ -1971,7 +1971,7 @@ internal class SwapInteractorImpl @Inject constructor( gasLimit = 1.toBigInteger(), fee = demoFee.copy(value = normalDemoFee), - ), + ), priorityFee = ProxyFee.Common( gasLimit = 1.toBigInteger(), fee = demoFee.copy(value = priorityDemoFee), diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 05a1fa9451..e7d4edd818 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -1029,8 +1029,7 @@ internal class StateBuilder( val fromFiatAmount = getFormattedFiatAmount(fromCryptoCurrency.value.fiatRate?.multiply(fromAmount)) val toFiatAmount = getFormattedFiatAmount(toCryptoCurrency.value.fiatRate?.multiply(toAmount)) - val shouldShowStatus = providerState.type == ExchangeProviderType.CEX.providerName || - providerState.type == ExchangeProviderType.DEX_BRIDGE.providerName + val shouldShowStatus = providerState.type == ExchangeProviderType.CEX.providerName return uiState.copy( successState = SwapSuccessStateHolder( timestamp = swapTransactionState.timestamp, From b168f321b8f35f12f719d169d0d4b082a3a97806 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 9 Jul 2024 14:39:17 +0100 Subject: [PATCH 03/17] Updated on 2026-08-14 --- .../domain/walletconnect/WalletConnectSdkHelper.kt | 4 ++-- .../express/models/response/ExchangeStatusResponse.kt | 8 ++++---- .../local/swaptx/SwapTransactionStatusStore.kt | 3 +++ .../swap/domain/models/domain/ExchangeStatus.kt | 1 + .../tangem/feature/swap/domain/SwapInteractorImpl.kt | 9 ++++----- .../tokendetails/viewmodels/ExchangeStatusFactory.kt | 11 ++++++++--- .../kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt | 2 +- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index abb7d6ce7e..8fae8e9abb 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -177,9 +177,9 @@ class WalletConnectSdkHelper { is Result.Success -> gasLimitResult.data.toBigDecimal().multiply(BigDecimal("1.2")) is Result.Failure -> { (gasLimitResult.error as? Throwable)?.let { Timber.e(it, "getGasLimit failed") } - BigDecimal(DEFAULT_MAX_GASLIMIT) // Set high gasLimit if not provided + DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided } - else -> BigDecimal(DEFAULT_MAX_GASLIMIT) // Set high gasLimit if not provided + else -> DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided } } diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt index dc786b02de..5b43c9cea5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt @@ -7,14 +7,14 @@ data class ExchangeStatusResponse( @Json(name = "providerId") val providerId: String, - @Json(name = "externalTxId") - val externalTxId: String, - @Json(name = "status") val status: ExchangeStatus, + @Json(name = "externalTxId") + val externalTxId: String?, + @Json(name = "externalTxUrl") - val externalTxUrl: String, + val externalTxUrl: String?, @Json(name = "error") val error: ExchangeStatusError?, diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt index 2bcb9a3dbb..cf99b7c4ac 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt @@ -10,9 +10,12 @@ interface SwapTransactionStatusStore { } enum class ExchangeAnalyticsStatus(val value: String) { + WaitingTxHash("Waiting tx hash"), InProgress("In Progress"), Done("Done"), Fail("Fail"), + FailTx("Fail tx"), + Unknown("Unknown"), KYC("KYC"), Refunded("Refunded"), Cancelled("Canceled"), diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt index c52f37ce6c..672d676fbb 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt @@ -20,5 +20,6 @@ enum class ExchangeStatus { Finished, Refunded, Cancelled, + TxFailed, Unknown, } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 25d18c1f45..c4cb77423f 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -317,8 +317,8 @@ internal class SwapInteractorImpl @Inject constructor( val fromTokenAddress = getTokenAddress(fromToken.currency) val isAllowedToSpend = quotes.fold( - ifRight = { - it.allowanceContract?.let { + ifRight = { quotes -> + quotes.allowanceContract?.let { isAllowedToSpend(networkId, fromToken.currency, amount, it) } ?: true }, @@ -870,7 +870,7 @@ internal class SwapInteractorImpl @Inject constructor( ) blockchain == Blockchain.Aptos -> { val gasUnitPrice = fee.feeValue.divide( - BigDecimal(fee.gasLimit), + fee.gasLimit.toBigDecimal(), Blockchain.Aptos.decimals(), RoundingMode.HALF_UP, ) @@ -1970,8 +1970,7 @@ internal class SwapInteractorImpl @Inject constructor( normalFee = ProxyFee.Common( gasLimit = 1.toBigInteger(), fee = demoFee.copy(value = normalDemoFee), - - ), + ), priorityFee = ProxyFee.Common( gasLimit = 1.toBigInteger(), fee = demoFee.copy(value = priorityDemoFee), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index e77952c869..54356f4665 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -156,23 +156,28 @@ internal class ExchangeStatusFactory( ) } - private fun ExchangeStatus?.isTerminal() = - this == ExchangeStatus.Refunded || this == ExchangeStatus.Finished || this == ExchangeStatus.Cancelled + private fun ExchangeStatus?.isTerminal() = this == ExchangeStatus.Refunded || + this == ExchangeStatus.Finished || + this == ExchangeStatus.Cancelled || + this == ExchangeStatus.TxFailed || + this == ExchangeStatus.Unknown private fun toAnalyticStatus(status: ExchangeStatus?): ExchangeAnalyticsStatus? { return when (status) { ExchangeStatus.New, ExchangeStatus.Waiting, - ExchangeStatus.WaitingTxHash, ExchangeStatus.Sending, ExchangeStatus.Confirming, ExchangeStatus.Exchanging, -> ExchangeAnalyticsStatus.InProgress + ExchangeStatus.WaitingTxHash -> ExchangeAnalyticsStatus.WaitingTxHash ExchangeStatus.Verifying -> ExchangeAnalyticsStatus.KYC ExchangeStatus.Failed -> ExchangeAnalyticsStatus.Fail + ExchangeStatus.TxFailed -> ExchangeAnalyticsStatus.FailTx ExchangeStatus.Finished -> ExchangeAnalyticsStatus.Done ExchangeStatus.Refunded -> ExchangeAnalyticsStatus.Refunded ExchangeStatus.Cancelled -> ExchangeAnalyticsStatus.Cancelled + ExchangeStatus.Unknown -> ExchangeAnalyticsStatus.Unknown else -> null } } diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt index 4b129ed3c4..620ff5a6c2 100644 --- a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt @@ -5,7 +5,7 @@ import java.math.BigDecimal import java.math.BigInteger internal fun BigInteger.toBigDecimal(decimals: Int): BigDecimal { - return BigDecimal(this).movePointLeft(decimals) + return this.toBigDecimal().movePointLeft(decimals) } internal fun BigInteger.toInstant(): Instant { From 8c58005fed0d156cf28e1b32e68a136b4fc04c0e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 10 Jul 2024 16:42:11 +0200 Subject: [PATCH 04/17] Updated on 2026-08-14 --- app/build.gradle.kts | 4 ++-- .../ui/components/notifications/OkxPromoNotification.kt | 2 +- features/wallet/impl/build.gradle.kts | 2 +- .../wallet/ui/utils/ReviewManagerRequester.kt | 2 +- gradle/dependencies.toml | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8615f2502..18fd29dc28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -184,8 +184,8 @@ dependencies { /** Other libraries */ implementation(deps.kotlin.immutable.collections) implementation(deps.material) - implementation(deps.googlePlay.core) - implementation(deps.googlePlay.core.ktx) + implementation(deps.googlePlay.review) + implementation(deps.googlePlay.review.ktx) implementation(deps.googlePlay.services.wallet) coreLibraryDesugaring(deps.desugar) implementation(deps.timber) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/OkxPromoNotification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/OkxPromoNotification.kt index 9d6d403946..eacaa9567e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/OkxPromoNotification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/OkxPromoNotification.kt @@ -58,7 +58,7 @@ private fun Content(config: NotificationConfig) { .align(Alignment.CenterVertically), ) Column( - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), modifier = Modifier .weight(1f) .padding(TangemTheme.dimens.spacing12), diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index dad4976a7d..ecad0b9c68 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { /** Other libraries */ implementation(deps.arrow.core) - implementation(deps.googlePlay.core) + implementation(deps.googlePlay.review) implementation(deps.jodatime) implementation(deps.kotlin.immutable.collections) implementation(deps.reKotlin) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ReviewManagerRequester.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ReviewManagerRequester.kt index a52e61dd42..1826afb74d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ReviewManagerRequester.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ReviewManagerRequester.kt @@ -3,10 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.ui.utils import android.app.Activity import android.content.Context import android.content.ContextWrapper +import com.google.android.gms.tasks.Task import com.google.android.play.core.review.ReviewInfo import com.google.android.play.core.review.ReviewManager import com.google.android.play.core.review.ReviewManagerFactory -import com.google.android.play.core.tasks.Task import timber.log.Timber internal object ReviewManagerRequester { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e73b861d62..41817d438f 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -46,8 +46,8 @@ coroutine = "1.7.2" desugarJdkLibs = "1.1.5" firebase = "26.0.0" googleMaterialComponent = "1.6.1" -googlePlayCore = "1.10.3" -googlePlayCoreKtx = "1.8.1" +googlePlayReview = "2.0.1" +googlePlayReviewKtx = "2.0.1" googlePlayServicesWallet = "19.1.0" hilt = "2.46" hilt-navigation = "1.0.0" @@ -212,8 +212,8 @@ kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", kotlin-coroutines-rx2 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2", version.ref = "coroutine" } kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } -googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" } -googlePlay-core-ktx = { module = "com.google.android.play:core-ktx", version.ref = "googlePlayCoreKtx" } +googlePlay-review = { module = "com.google.android.play:review", version.ref = "googlePlayReview" } +googlePlay-review-ktx = { module = "com.google.android.play:review-ktx", version.ref = "googlePlayReviewKtx" } googlePlay-services-wallet = { module = "com.google.android.gms:play-services-wallet", version.ref = "googlePlayServicesWallet" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-core = { module = "com.google.dagger:hilt-core", version.ref = "hilt" } From 933dfb16e743da26edf199f5a7592b78b3c75a73 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 Jul 2024 15:39:24 +0200 Subject: [PATCH 05/17] Updated on 2026-08-14 --- .../feature/swap/domain/models/ui/SwapState.kt | 1 - .../tangem/feature/swap/domain/SwapInteractorImpl.kt | 12 ++++++------ .../feature/swap/ui/SwapPermissionBottomSheet.kt | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index d7ba6d0ac0..2616e04be8 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -26,7 +26,6 @@ sealed interface SwapState { val permissionState: PermissionDataState = PermissionDataState.Empty, val swapDataModel: SwapDataModel? = null, val txFee: TxFeeState, - // val txFeeIncludeOtherNativeFee: TxFeeState, val warnings: List = emptyList(), val swapProvider: SwapProvider, ) : SwapState diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index c4cb77423f..c1be6a25bd 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1529,7 +1529,7 @@ internal class SwapInteractorImpl @Inject constructor( amount = priorityFeeValue, decimals = normalFee.fee.decimals, ) - // + // endregion // region fees include otherNativeFee val feesFiatWithNative = getFormattedFiatFees( fromToken = fromToken, @@ -1548,7 +1548,7 @@ internal class SwapInteractorImpl @Inject constructor( amount = priorityFeeValue + otherNativeFeeValue, decimals = normalFee.fee.decimals, ) - // + // endregion return TxFeeState.MultipleFeeState( normalFee = TxFee( feeValue = normalFeeValue, @@ -1602,7 +1602,7 @@ internal class SwapInteractorImpl @Inject constructor( amount = normalFeeValue + otherNativeFeeValue, decimals = singleFee.fee.decimals, ) - // + // endregion return TxFeeState.SingleFeeState( fee = TxFee( feeValue = normalFeeValue, @@ -1645,7 +1645,7 @@ internal class SwapInteractorImpl @Inject constructor( // region otherNativeFee val normalFeeWithOtherNative = feeNormal + otherNativeFeeValue - val priorityFeeWithOtherNative = feeNormal + otherNativeFeeValue + val priorityFeeWithOtherNative = feePriority + otherNativeFeeValue val normalFiatValueWithNative = getFormattedFiatFees(fromToken, normalFeeWithOtherNative)[0] val priorityFiatValueWithNative = getFormattedFiatFees(fromToken, priorityFeeWithOtherNative)[0] @@ -1657,7 +1657,7 @@ internal class SwapInteractorImpl @Inject constructor( amount = priorityFeeWithOtherNative, decimals = priorityFee.amount.decimals, ) - // + // endregion TxFeeState.MultipleFeeState( normalFee = TxFee( feeValue = feeNormal, @@ -1700,7 +1700,7 @@ internal class SwapInteractorImpl @Inject constructor( amount = normalFeeWithOtherNative, decimals = this.normal.amount.decimals, ) - // + // endregion TxFeeState.SingleFeeState( fee = TxFee( feeValue = this.normal.amount.value ?: BigDecimal.ZERO, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt index 1915863d98..6c06e7e630 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt @@ -304,7 +304,7 @@ private fun Preview_AgreementBottomSheet() { private val previewData = GivePermissionBottomSheetConfig( data = SwapPermissionState.ReadyForRequest( - providerName = "1icnh", + providerName = "1inch", currency = "DAI", amount = "∞", walletAddress = "", From 62545c47e86fcca96e68dd7d799b995a6be599b6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 Jul 2024 12:04:40 +0300 Subject: [PATCH 06/17] Updated on 2026-08-14 --- .../tangem/core/ui/components/transactions/TransactionList.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt index 6b4cc1bd3d..220b5c82ca 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt @@ -82,7 +82,9 @@ private fun LazyListScope.contentItems( when (item) { is TxHistoryState.TxHistoryItemState.GroupTitle -> item.itemKey is TxHistoryState.TxHistoryItemState.Title -> item.onExploreClick.hashCode() - is TxHistoryState.TxHistoryItemState.Transaction -> item.state.txHash + is TxHistoryState.TxHistoryItemState.Transaction -> + item.state.txHash + + ((item.state as? TransactionState.Content)?.hashCode() ?: "") } }, contentType = txHistoryItems.itemContentType { it::class.java }, From e6d7bee87a5cd50c03570e06dd19394b4ae6876b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Jul 2024 13:21:11 +0500 Subject: [PATCH 07/17] Updated on 2026-08-14 --- .../DefaultLegacyWalletConnectRepository.kt | 3 ++- .../domain/LegacyWalletConnectRepository.kt | 2 ++ .../domain/WalletConnectInteractor.kt | 20 +++++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt index acee05571c..4f9055bfb4 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt @@ -35,7 +35,8 @@ internal class DefaultLegacyWalletConnectRepository( private val _activeSessions: MutableSharedFlow> = MutableSharedFlow() override val activeSessions: Flow> = _activeSessions - private var currentSessions: List = emptyList() + override var currentSessions: List = emptyList() + private set /** * @param projectId Project ID at https://cloud.walletconnect.com/ diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt index 9096e879e0..616643a960 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt @@ -9,6 +9,8 @@ interface LegacyWalletConnectRepository { val activeSessions: Flow> + val currentSessions: List + fun init(projectId: String) fun setUserNamespaces(userNamespaces: Map>) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index 66f8b5d16c..ae9d50d6cf 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain.walletconnect2.domain +import android.net.Uri import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.domain.tokens.model.CryptoCurrency @@ -132,6 +133,7 @@ class WalletConnectInteractor( runCatching { if (accounts.isEmpty()) return isWalletConnectReadyForDeepLinks = true + if (deeplinkStack.empty()) return val lastDeeplink = deeplinkStack.pop() store.dispatchOnMain(WalletConnectAction.OpenSession(lastDeeplink)) }.onFailure { @@ -361,6 +363,18 @@ class WalletConnectInteractor( * @param deeplink deeplink to handle */ fun addDeeplink(deeplink: String) { + val deeplinkQueries = deeplink.split(WC_SPLIT_CHAR).lastOrNull() + val sessionTopic = deeplinkQueries?.let { Uri.parse(it).getQueryParameter(WC_TOPIC_QUERY_NAME) } + + val isAlreadyActiveSessionTopic = walletConnectRepository.currentSessions.any { session -> + session.topic == sessionTopic + } + + if (isAlreadyActiveSessionTopic && deeplinkQueries != null) { + Timber.i("WC already has an active session topic: $deeplink") + return + } + if (isWalletConnectReadyForDeepLinks) { store.dispatchOnMain(WalletConnectAction.OpenSession(deeplink)) } else { @@ -406,7 +420,9 @@ class WalletConnectInteractor( return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId) } - companion object { - private const val WC_SCHEME = "wc" + private companion object { + const val WC_SCHEME = "wc" + const val WC_TOPIC_QUERY_NAME = "sessionTopic" + const val WC_SPLIT_CHAR = "/" } } \ No newline at end of file From ec7b018eedd48080bfcfd363790bce468c177748 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Jul 2024 16:27:33 +0500 Subject: [PATCH 08/17] Updated on 2026-08-14 --- .../walletconnect2/domain/WalletConnectInteractor.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index ae9d50d6cf..1f5dce9663 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -1,6 +1,5 @@ package com.tangem.tap.domain.walletconnect2.domain -import android.net.Uri import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.domain.tokens.model.CryptoCurrency @@ -363,14 +362,15 @@ class WalletConnectInteractor( * @param deeplink deeplink to handle */ fun addDeeplink(deeplink: String) { - val deeplinkQueries = deeplink.split(WC_SPLIT_CHAR).lastOrNull() - val sessionTopic = deeplinkQueries?.let { Uri.parse(it).getQueryParameter(WC_TOPIC_QUERY_NAME) } + val deeplinkRegex = Regex(WC_PARAM_REGEX) + val matched = deeplinkRegex.findAll(deeplink) + val sessionTopic = matched.firstOrNull { it.value.contains(WC_TOPIC_QUERY_NAME) }?.groupValues?.lastOrNull() val isAlreadyActiveSessionTopic = walletConnectRepository.currentSessions.any { session -> session.topic == sessionTopic } - if (isAlreadyActiveSessionTopic && deeplinkQueries != null) { + if (isAlreadyActiveSessionTopic && sessionTopic != null) { Timber.i("WC already has an active session topic: $deeplink") return } @@ -423,6 +423,6 @@ class WalletConnectInteractor( private companion object { const val WC_SCHEME = "wc" const val WC_TOPIC_QUERY_NAME = "sessionTopic" - const val WC_SPLIT_CHAR = "/" + const val WC_PARAM_REGEX = "([a-zA-Z\\d-]+)=([a-zA-Z\\d]+)" } } \ No newline at end of file From 8fc579ba5cdc5a2198c7c07e85f462d10057a1d0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 Jul 2024 15:31:02 +0300 Subject: [PATCH 09/17] Updated on 2026-08-14 --- .../models/response/ExchangeStatusResponse.kt | 6 ++ .../repository/DefaultCurrenciesRepository.kt | 49 +++++++++++++++- .../tokens/AddCryptoCurrenciesUseCase.kt | 57 +++++++++++++++++-- .../tokens/repository/CurrenciesRepository.kt | 10 ++++ .../repository/MockCurrenciesRepository.kt | 8 +++ .../converters/ExchangeStatusConverter.kt | 2 + .../domain/models/domain/ExchangeStatus.kt | 2 + .../state/SwapTransactionsState.kt | 1 + .../components/ExchangeStatusNotifications.kt | 17 ++++++ ...enDetailsSwapTransactionsStateConverter.kt | 34 ++++++++--- .../exchange/ExchangeStatusBlock.kt | 6 ++ .../viewmodels/ExchangeStatusFactory.kt | 46 +++++++++++---- .../viewmodels/TokenDetailsClickIntents.kt | 2 + .../viewmodels/TokenDetailsViewModel.kt | 15 ++++- 14 files changed, 228 insertions(+), 27 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt index 5b43c9cea5..7682c70d4f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt @@ -18,6 +18,12 @@ data class ExchangeStatusResponse( @Json(name = "error") val error: ExchangeStatusError?, + + @Json(name = "refundNetwork") + val refundNetwork: String? = null, + + @Json(name = "refundContractAddress") + val refundContractAddress: String? = null, ) enum class ExchangeStatus { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 9459984032..214d9721e1 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.withContext import timber.log.Timber import com.tangem.blockchain.common.FeePaidCurrency as FeePaidSdkCurrency -@Suppress("LargeClass", "LongParameterList") +@Suppress("LargeClass", "LongParameterList", "TooManyFunctions") internal class DefaultCurrenciesRepository( private val tangemTechApi: TangemTechApi, private val tangemExpressApi: TangemExpressApi, @@ -53,6 +53,7 @@ internal class DefaultCurrenciesRepository( private val demoConfig = DemoConfig() private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory() + private val cryptoCurrencyFactory = CryptoCurrencyFactory() private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig) private val userTokensResponseFactory = UserTokensResponseFactory() private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility() @@ -125,7 +126,7 @@ internal class DefaultCurrenciesRepository( return newTokens .filterNot { savedCurrencies.hasCoinForToken(it) } // tokens without coins .mapNotNull { - CryptoCurrencyFactory().createCoin( + cryptoCurrencyFactory.createCoin( blockchain = getBlockchain(networkId = it.network.id), extraDerivationPath = it.network.derivationPath.value, derivationStyleProvider = getUserWallet(userWalletId).scanResponse.derivationStyleProvider, @@ -413,12 +414,54 @@ internal class DefaultCurrenciesRepository( } override fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token { - return CryptoCurrencyFactory().createToken( + return cryptoCurrencyFactory.createToken( cryptoCurrency = cryptoCurrency, network = network, ) } + override suspend fun createTokenCurrency( + userWalletId: UserWalletId, + contractAddress: String, + networkId: String, + ): CryptoCurrency.Token { + val userWallet = getUserWallet(userWalletId) + val token = withContext(dispatchers.io) { + val foundToken = tangemTechApi.getCoins( + contractAddress = contractAddress, + networkIds = networkId, + ) + .getOrThrow() + .coins + .firstNotNullOfOrNull { coin -> + val networksWithTheSameAddress = coin.networks.filter { network -> + (network.contractAddress != null || network.decimalCount != null) && + network.contractAddress?.equals(contractAddress, ignoreCase = true) == true + } + + if (networksWithTheSameAddress.isNotEmpty()) { + coin.copy(networks = networksWithTheSameAddress) + } else { + null + } + } ?: error("Token not found") + val network = foundToken.networks.firstOrNull { it.networkId == networkId } ?: error("Network not found") + CryptoCurrencyFactory.Token( + symbol = foundToken.symbol, + name = foundToken.name, + contractAddress = contractAddress, + decimals = network.decimalCount?.toInt() ?: error("Decimals not found"), + id = foundToken.id, + ) + } + return cryptoCurrencyFactory.createToken( + token = token, + networkId = networkId, + extraDerivationPath = null, + derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, + ) ?: error("Unable to create token") + } + private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { return userTokensStore.get(userWallet.walletId).map { storedTokens -> responseCurrenciesFactory.createCurrencies( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt index e8c6f0b7a8..01223c61a3 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt @@ -79,13 +79,35 @@ class AddCryptoCurrenciesUseCase( .toNonEmptyListOrNull() ?: return@either - catch({ currenciesRepository.addCurrencies(userWalletId, currenciesToAdd) }) { - raise(it) - } - + addCurrencies(userWalletId, currenciesToAdd) refreshUpdatedNetworks(userWalletId, currenciesToAdd, existingCurrencies) } + suspend operator fun invoke( + userWalletId: UserWalletId, + contractAddress: String, + networkId: String, + ): Either = either { + val existingCurrencies = + catch({ currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) }) { + raise(it) + } + val foundToken = existingCurrencies + .filterIsInstance() + .firstOrNull { + it.network.backendId == networkId && + !it.isCustom && + it.contractAddress.equals(contractAddress, true) + } + if (foundToken != null) { + return@either foundToken + } + val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId) + addCurrencies(userWalletId, listOf(tokenToAdd)) + refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies) + tokenToAdd + } + /** * Refreshes the network statuses for tokens that have corresponding coins in the * [existingCurrencies] list. @@ -117,6 +139,33 @@ class AddCryptoCurrenciesUseCase( } } + private suspend fun Raise.createTokenCurrency( + userWalletId: UserWalletId, + contractAddress: String, + networkId: String, + ): CryptoCurrency.Token { + return catch( + block = { + currenciesRepository.createTokenCurrency( + userWalletId = userWalletId, + contractAddress = contractAddress, + networkId = networkId, + ) + }, + catch = { + raise(it) + }, + ) + } + + private suspend fun Raise.addCurrencies(userWalletId: UserWalletId, tokens: List) { + catch( + { currenciesRepository.addCurrencies(userWalletId, tokens) }, + ) { + raise(it) + } + } + /** * Determines if the [existingCurrencies] list contains a coin that corresponds * to the given [token]. diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 5bdf7b4067..d5daebcd8e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.Flow /** * Repository for everything related to the tokens of user wallet * */ +@Suppress("TooManyFunctions") interface CurrenciesRepository { /** @@ -210,4 +211,13 @@ interface CurrenciesRepository { * Creates token [cryptoCurrency] based on current token and [network] it`s will be added */ fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token + + /** + * Creates token [cryptoCurrency] based on [contractAddress] and [networkId] it`s will be added + */ + suspend fun createTokenCurrency( + userWalletId: UserWalletId, + contractAddress: String, + networkId: String, + ): CryptoCurrency.Token } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index d4d616d88a..ff73d40462 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -136,4 +136,12 @@ internal class MockCurrenciesRepository( override fun createTokenCurrency(cryptoCurrency: CryptoCurrency.Token, network: Network): CryptoCurrency.Token { return cryptoCurrency } + + override suspend fun createTokenCurrency( + userWalletId: UserWalletId, + contractAddress: String, + networkId: String, + ): CryptoCurrency.Token { + error("not implemented") + } } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExchangeStatusConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExchangeStatusConverter.kt index 0a566d5500..d782b606f9 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExchangeStatusConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExchangeStatusConverter.kt @@ -15,6 +15,8 @@ internal class ExchangeStatusConverter : Converter Unit, val onGoToProviderClick: (String) -> Unit, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt index ac825983b8..58b0829319 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt @@ -3,6 +3,8 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.componen import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.features.tokendetails.impl.R @Immutable @@ -35,4 +37,19 @@ internal sealed class ExchangeStatusNotifications(val config: NotificationConfig ), ), ) + + data class TokenRefunded( + val cryptoCurrency: CryptoCurrency, + val onGoToTokenClick: () -> Unit, + ) : ExchangeStatusNotifications( + config = NotificationConfig( + title = stringReference("TITLE FOR TOKEN REFUND"), + subtitle = stringReference("SUBTITLE FOR TOKEN REFUND"), + iconResId = R.drawable.ic_alert_triangle_20, + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = stringReference("Go to token"), + onClick = onGoToTokenClick, + ), + ), + ) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index 6c1a71f7af..fba5085305 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -62,7 +62,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( }?.fiatRate?.multiply(fromAmount) val timestamp = transaction.timestamp val notifications = - getNotification(transaction.status?.status, transaction.status?.txExternalUrl) + getNotification(transaction.status?.status, transaction.status?.txExternalUrl, null) val showProviderLink = getShowProviderLink(notifications, transaction.status) result.add( SwapTransactionsState( @@ -77,10 +77,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( statuses = getStatuses(transaction.status?.status), hasFailed = transaction.status?.status == ExchangeStatus.Failed, activeStatus = transaction.status?.status, - notification = getNotification( - transaction.status?.status, - transaction.status?.txExternalUrl, - ), + notification = notifications, toCryptoCurrency = toCryptoCurrency, toCryptoAmount = BigDecimalFormatter.formatCryptoAmount( cryptoAmount = toAmount, @@ -110,10 +107,15 @@ internal class TokenDetailsSwapTransactionsStateConverter( return result.toPersistentList() } - fun updateTxStatus(tx: SwapTransactionsState, statusModel: ExchangeStatusModel?): SwapTransactionsState { + fun updateTxStatus( + tx: SwapTransactionsState, + statusModel: ExchangeStatusModel?, + refundToken: CryptoCurrency?, + isRefundTerminalStatus: Boolean, + ): SwapTransactionsState { if (statusModel == null || tx.activeStatus == statusModel.status) return tx val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed - val notifications = getNotification(statusModel.status, statusModel.txExternalUrl) + val notifications = getNotification(statusModel.status, statusModel.txExternalUrl, refundToken) val showProviderLink = getShowProviderLink(notifications, statusModel) return tx.copy( activeStatus = statusModel.status, @@ -122,6 +124,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( statuses = getStatuses(statusModel.status, hasFailed), txUrl = statusModel.txExternalUrl, showProviderLink = showProviderLink, + isRefundTerminalStatus = isRefundTerminalStatus, ) } @@ -133,7 +136,11 @@ internal class TokenDetailsSwapTransactionsStateConverter( ) } - private fun getNotification(status: ExchangeStatus?, txUrl: String?): ExchangeStatusNotifications? { + private fun getNotification( + status: ExchangeStatus?, + txUrl: String?, + refundToken: CryptoCurrency?, + ): ExchangeStatusNotifications? { if (txUrl == null) return null return when (status) { ExchangeStatus.Failed -> { @@ -152,6 +159,15 @@ internal class TokenDetailsSwapTransactionsStateConverter( clickIntents.onGoToProviderClick(txUrl) } } + ExchangeStatus.Refunded -> { + if (refundToken == null) { + null + } else { + ExchangeStatusNotifications.TokenRefunded(refundToken) { + clickIntents.onGoToRefundedTokenClick(refundToken) + } + } + } else -> null } } @@ -287,7 +303,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( status = ExchangeStatus.Refunded, text = TextReference.Res(R.string.express_exchange_status_refunded), isActive = false, - isDone = isRefunded, + isDone = false, ) else -> ExchangeStatusState( status = ExchangeStatus.Sending, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt index c715a6c6bd..ff2dd3baff 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt @@ -115,6 +115,11 @@ private fun ExchangeStatusStep( color = TangemTheme.colors.icon.warning, isDone = it.isDone, ) + it.status == ExchangeStatus.Refunded -> ExchangeStep( + iconRes = R.drawable.ic_close_24, + color = TangemTheme.colors.icon.warning, + isDone = it.isDone, + ) it.status == ExchangeStatus.Verifying -> ExchangeStep( iconRes = R.drawable.ic_exclamation_24, color = TangemTheme.colors.icon.attention, @@ -141,6 +146,7 @@ private fun ExchangeStatusStep( private fun ExchangeStatusStepText(stepStatus: ExchangeStatusState) { val textColor = when { stepStatus.status == ExchangeStatus.Cancelled -> TangemTheme.colors.icon.warning + stepStatus.status == ExchangeStatus.Refunded -> TangemTheme.colors.icon.warning stepStatus.status == ExchangeStatus.Failed && !stepStatus.isDone -> TangemTheme.colors.icon.warning stepStatus.status == ExchangeStatus.Verifying && !stepStatus.isDone -> TangemTheme.colors.icon.attention stepStatus.isDone -> TangemTheme.colors.text.primary1 diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index 54356f4665..e4118e7141 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -4,6 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.swaptx.ExchangeAnalyticsStatus import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.models.analytics.TokenExchangeAnalyticsEvent @@ -38,6 +39,7 @@ internal class ExchangeStatusFactory( private val swapRepository: SwapRepository, private val quotesRepository: QuotesRepository, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val swapTransactionStatusStore: SwapTransactionStatusStore, private val dispatchers: CoroutineDispatcherProvider, private val clickIntents: TokenDetailsClickIntents, @@ -85,7 +87,7 @@ internal class ExchangeStatusFactory( val bottomSheetConfig = state.bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return state val selectedTx = bottomSheetConfig.value - return if (selectedTx.activeStatus.isTerminal()) { + return if (selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus)) { swapTransactionRepository.removeTransaction( userWalletId = userWalletId, fromCryptoCurrency = selectedTx.fromCryptoCurrency, @@ -104,12 +106,19 @@ internal class ExchangeStatusFactory( suspend fun updateSwapTxStatuses(swapTxList: PersistentList) = withContext(dispatchers.io) { swapTxList.map { tx -> async { - if (tx.activeStatus.isTerminal()) { + val statusModel = getExchangeStatus(tx.txId) + val isRefundTerminalStatus = statusModel?.refundNetwork == null && + statusModel?.refundContractAddress == null + if (tx.activeStatus.isTerminal(isRefundTerminalStatus)) { tx } else { - val statusModel = getExchangeStatus(tx.txId) - swapTransactionsStateConverter - .updateTxStatus(tx, statusModel) + val addedRefundToken = addRefundCurrencyIfNeeded(statusModel) + swapTransactionsStateConverter.updateTxStatus( + tx = tx, + statusModel = statusModel, + refundToken = addedRefundToken, + isRefundTerminalStatus = isRefundTerminalStatus, + ) } } } @@ -142,6 +151,20 @@ internal class ExchangeStatusFactory( } } + private suspend fun addRefundCurrencyIfNeeded(status: ExchangeStatusModel?): CryptoCurrency? { + status ?: return null + val refundNetwork = status.refundNetwork + val refundContractAddress = status.refundContractAddress + if (refundNetwork != null && refundContractAddress != null) { + return addCryptoCurrenciesUseCase( + userWalletId = userWalletId, + contractAddress = refundContractAddress, + networkId = refundNetwork, + ).getOrNull() + } + return null + } + private fun getExchangeStatusState( savedTransactions: List?, quotes: Set, @@ -156,11 +179,14 @@ internal class ExchangeStatusFactory( ) } - private fun ExchangeStatus?.isTerminal() = this == ExchangeStatus.Refunded || - this == ExchangeStatus.Finished || - this == ExchangeStatus.Cancelled || - this == ExchangeStatus.TxFailed || - this == ExchangeStatus.Unknown + private fun ExchangeStatus?.isTerminal(isRefundTerminal: Boolean): Boolean { + val needTerminalRefund = this == ExchangeStatus.Refunded && isRefundTerminal + return needTerminalRefund || + this == ExchangeStatus.Finished || + this == ExchangeStatus.Cancelled || + this == ExchangeStatus.TxFailed || + this == ExchangeStatus.Unknown + } private fun toAnalyticStatus(status: ExchangeStatus?): ExchangeAnalyticsStatus? { return when (status) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 751b8dcd92..387d2f3482 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -55,4 +55,6 @@ interface TokenDetailsClickIntents { fun onCopyAddress(): TextReference? fun onAssociateClick() + + fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 4d115db24e..9fa0481605 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -96,6 +96,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase, private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase, @@ -156,6 +157,7 @@ internal class TokenDetailsViewModel @Inject constructor( swapRepository = swapRepository, quotesRepository = quotesRepository, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase, swapTransactionStatusStore = swapTransactionStatusStore, dispatchers = dispatchers, clickIntents = this, @@ -691,7 +693,8 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onDismissBottomSheet() { - if (internalUiState.value.bottomSheetConfig?.content is ExchangeStatusBottomSheetConfig) { + val bsContent = internalUiState.value.bottomSheetConfig?.content + if (bsContent is ExchangeStatusBottomSheetConfig) { viewModelScope.launch(dispatchers.main) { internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed() } @@ -713,6 +716,16 @@ internal class TokenDetailsViewModel @Inject constructor( router.openUrl(url) } + override fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) { + if (internalUiState.value.bottomSheetConfig?.content is ExchangeStatusBottomSheetConfig) { + viewModelScope.launch(dispatchers.main) { + internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed() + } + } + internalUiState.value = stateFactory.getStateWithClosedBottomSheet() + router.openTokenDetails(userWalletId, cryptoCurrency) + } + override fun onSwapPromoDismiss() { viewModelScope.launch(dispatchers.main) { shouldShowSwapPromoTokenUseCase.neverToShow() From 2cd0164d1d51500e873cba7b627b2222b6e43518 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Jul 2024 17:39:43 +0300 Subject: [PATCH 10/17] Updated on 2026-08-14 --- .../viewmodels/ExchangeStatusFactory.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index e4118e7141..152575c441 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -13,6 +13,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.swap.domain.SwapTransactionRepository import com.tangem.feature.swap.domain.api.SwapRepository +import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel @@ -108,11 +109,12 @@ internal class ExchangeStatusFactory( async { val statusModel = getExchangeStatus(tx.txId) val isRefundTerminalStatus = statusModel?.refundNetwork == null && - statusModel?.refundContractAddress == null + statusModel?.refundContractAddress == null && + tx.provider.type != ExchangeProviderType.DEX_BRIDGE if (tx.activeStatus.isTerminal(isRefundTerminalStatus)) { tx } else { - val addedRefundToken = addRefundCurrencyIfNeeded(statusModel) + val addedRefundToken = addRefundCurrencyIfNeeded(statusModel, tx.provider.type) swapTransactionsStateConverter.updateTxStatus( tx = tx, statusModel = statusModel, @@ -151,8 +153,15 @@ internal class ExchangeStatusFactory( } } - private suspend fun addRefundCurrencyIfNeeded(status: ExchangeStatusModel?): CryptoCurrency? { + /** + * For now do it only for dex-bridge provider + */ + private suspend fun addRefundCurrencyIfNeeded( + status: ExchangeStatusModel?, + type: ExchangeProviderType, + ): CryptoCurrency? { status ?: return null + if (type != ExchangeProviderType.DEX_BRIDGE) return null val refundNetwork = status.refundNetwork val refundContractAddress = status.refundContractAddress if (refundNetwork != null && refundContractAddress != null) { From 5016ee56eab6c52fc22548ca49dabe10bae7d9cd Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 26 Jul 2024 11:14:52 +0300 Subject: [PATCH 11/17] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 1497 +++++++++-------- core/res/src/main/res/values/strings.xml | 1479 ++++++++-------- .../notifications/CurrencyNotification.kt | 125 ++ .../CurrencyNotificationConfig.kt | 35 + .../components/notifications/Notification.kt | 60 +- .../components/ExchangeStatusNotifications.kt | 83 +- ...enDetailsSwapTransactionsStateConverter.kt | 17 +- .../exchange/ExchangeStatusBottomSheet.kt | 56 +- .../viewmodels/TokenDetailsClickIntents.kt | 2 + .../viewmodels/TokenDetailsViewModel.kt | 4 + 10 files changed, 1798 insertions(+), 1560 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotification.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotificationConfig.kt diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 3ee8a28d64..1400f9c41a 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1,750 +1,753 @@ - Добавить токен - Валюты - Отправляйте только %1$s (%2$s) в сети %3$s на этот адрес. Использование другой сети может привести к утрате средств. - Обратиться в поддержку - Эта функция недоступна в демонстрационном режиме - Причина: %s - Не могу отправить транзакцию - Выбранный кошелёк не поддерживает сеть %1$s - Для активации криптографии сети %1$s необходимо сбросить кошелек до заводских настроек. Пожалуйста, выведите свои средства, чтобы не потерять их, после сброса доступ к текущему кошельку будет невозможен. - Токены в сети %1$s не поддерживаются этой картой из-за ограничений прошивки. - У вас возникли трудности со сканированием карты? - Эта карта не предназначена для работы с этим приложением - Подключите функцию комиссии по умолчанию и при формировании транзакции на отправку средств комиссия будет выставлена автоматически, а экран комиссии пропущен. Вы всегда сможете на него вернуться. - Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem - Включите биометрическую аутентификацию - Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. - При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения. - Сохранение кода доступа - Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. - Cохранение кошелька - Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. - Тёмная - Светлая - Как в системе - Тема - Настройки приложения - Чтобы скрыть или показать баланс, просто поверните ваше устройство вниз или отключите опцию его в разделе \"Настройки\" - Больше не показывать - Понятно - Балансы скрыты - Пожалуйста, отсканируйте карту - Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту - Слишком много попыток - Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. - Начать резервное копирование - - %d карта - %d карты - %d карт - %d карт - - Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. - Использовать эту карту для сброса кода доступа на других картах в этом кошельке - Восстановление кода доступа - Сбросить - Вы уверены, что хотите это сделать? - Смена кода доступа - Код доступа будет изменен только на данной карте - Все карты выбранного кошелька сброшены до заводских настроек, вы можете создать новый кошелек - Сброс завершён - Хотите сбросить следующую карту от этого кошелька? - Сброс карты - Рекомендуем завершить процесс сброса всех карт кошелька - Вы сбросили не все карты - Заводские настройки - Тип безопасности - Настройки карты - Помимо сетевой комиссий, сеть Cardano взимает %1$s ADA при транзакции с токеном %2$s - Требования к транзакции Cardano - Чтобы совершить транзакцию %1$s, внесите некоторую сумму ADA для покрытия сетевой комиссии и минимального значения ADA (рекомендуется 5 ADA) - Недостаточно ADA для транзакции - Вы должны поддерживать некоторое количество ADA, поскольку у вас на балансе есть токены в сети Cardano - Недостаточно ADA - Принять - Доступ запрещен - Применить - Одобрение - Внимание - Баланс: %s - Баланс - биометрическую аутентификацию - биометрией - Купить - Перейти на %1$s - Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности. - Отмена - Закрыть - Продолжить - Копировать - Скопировать адрес - Создать - Удалить - Отключено - Готово - Включить - Включено - Ошибка - Обозреватель - Посмотреть историю транзакций - Обозреватель - Комиссия - Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s - Свое - Быстро - По рынку - Медленно - Скорость и комиссия - Получить адреса - К провайдеру - Импортировать - Позже - Заблокирован - Основная сеть - Сетевая комиссия - Сумма отправки будет уменьшена на %1$s (%2$s) для покрытия выбранного уровня комиссии - Далее - Нет - Нет адреса - OK - Основная карта - Парольная фраза - Вставить - Подробнее - Получить - Отклонить - Перезагрузить - Переименовать - Сохранить изменения - Искать - Поиск токенов - Seed-фраза - Выберите действие - Продать - Отправить - Сервер недоступен, повторите попытку позднее - Поделиться - Подписать - Подписать и отправить - Начать - Отправить - Успешно - Поддержка - Обмен - условия участия - Ошибка транзакции - Транзакции - Перевод - Я понял - Произошла ошибка. Пожалуйста, попробуйте снова. - Недоступно - Да - Адрес контракта скопирован! - Доступные сети - Добавить токен - Адрес контракта - Адрес контракта некорректен - Пожалуйста, выберите сеть - Десятичное число должно быть действительным целым числом, до %li - Своя деривация - Например m/00\'/0000\'/0\'/0/0 - Введите свою деривацию - Знаков после запятой - Путь деривации - По умолчанию - Деривация по BIP44 - Введенный путь деривации некорректен - Например, USD Coin - Название токена - Не выбрано - Сеть - Сеть - Вы можете добавить токен в ручную, если он не поддерживается Tangem - Например, USDC - Символ - Символ токена - Этот токен/сеть уже находится в вашем списке - Токены могут быть созданы кем угодно. Остерегайтесь мошеннических токенов, они могут ничего не стоить - Остерегайтесь мошеннических токенов, они могут ничего не стоить - Токены могут быть созданы кем угодно - Чат - Код доступа - Перед сканированием карты вам нужно будет ввести правильный код доступа. - Задержка сканирования - Этот механизм защищает карту от бесконтактных атак. Между сканированием карты и выполнением команды будет добавлена задержка. - Пароль - Перед выполнением любой команды, влекущей за собой изменение состояния карты, вам необходимо будет ввести пароль. - Реферальная программа - Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы - %s хэшей - Номер карты - Обратиться в поддержку - Добавить еще карты - Валюта приложения - Скрывать балансы жестом переворота - Эмитент - Подписано - Подробности - Проверьте подключение с интернетом или переключитесь на другую сеть - Условия использования - Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. - Мои токены - У вас нет добавленных токенов. Добавьте токены для обмена - Недоступен для обмена с %s - Предоставлено - Статус - Tangem предоставляет доступ к обмену через сторонних поставщиков в соответствии с их правилами - Выберите провайдера - Произошла ошибка. Код: %s - К сожалению, обмен указанной пары через выбранного провайдера на данный момент невозможен. Попробуйте совершить обмен позже. (Код: %s) - Выбранный провайдер недоступен для обмена. Попробуйте позже. (Код: %s) - В данный момент обмен невозможен. Попробуйте позже. (Код: %s) - Курс обмена - Обмен через %s - Чтобы вернуть ваши деньги, посетите сайт провайдера - Операция не выполнена провайдером - Посетите сайт провайдера для проверки - Провайдер запрашивает прохождение верификации - Отменен - Подтверждено - Подтверждение - Подтверждение... - Обменяно - Обмен - Обмен... - Неудачно - Депозит получен - Ожидание депозита - Ожидаем пополнения... - Возвращено - Отправляем - Отправка средств... - Отправлено - Данные провайдера. Сумма к получению может измениться в зависимости от рыночных условий. - Статус обмена - Требуется верификация - Ожидание хеша транзакции - Список токенов в вашем кошельке - Получение наилучших курсов... - Плавающая ставка - Пользуясь сервисом, вы соглашаетесь с %s - Пользуясь сервисом, вы соглашаетесь с %1$s и %2$s - Больше провайдеров на подходе.\nСледите за обновлениями! - Политикой конфиденциальности - Провайдер - Лучший курс - Доступно до %s - Доступно с %s - Недоступно для этой пары - Требуется разрешение - Рекомендовано - Условиями использования - Токены не найдены. Пожалуйста, попробуйте другой запрос - ID: %s - ID транзакции скопирован - Информация ниже не является обязательной. Вы можете стереть её, если хотите. - Расскажите, каких функций вам не хватает, и мы постараемся вам помочь. - Скажите, пожалуйста, какая у вас карта? - Привет, команда поддержки, - Пожалуйста, расскажите нам больше о вашей проблеме. Каждая маленькая деталь может помочь. - Мои предложения - Не могу отсканировать карту - Обращение в поддержку - Обращение в поддержку Tangem - Не могу отправить транзакцию - Купить - Сканировать - Чтобы изменить код доступа, приложите карту как показано выше и не убирайте до окончания операции - Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции - Чтобы создать кошелек, приложите карту как показано выше и не убирайте до окончания операции - Приложите карту #%s для сброса - Приложите, чтобы отсканировать - Приложите, чтобы подписать - Приложите карту - Вы обновили данные биометрии, отсканируйте свою карту для входа - Ваш баланс должен быть выше суммы комиссии для осуществления перевода - Недостаточно средств - У вас недостаточно Маны для этой транзакции. Пожалуйста, подождите, пока Мана восполнится. Ваш баланс маны равен %1$s/%2$s - Недостаточно Маны - Вы можете перевести только %s из-за ограничения Mana, установленного сетью Koinos - Лимит маны - Сеть Koinos использует Ману для оплаты комиссии сети. У вас есть %1$s/%2$s Mana - Уровень маны - Чтобы начать отслеживать свои криптоактивы и транзакции, добавьте токены - Управление токенами - Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту - Отсканируйте карту - Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля. - Обмен с Changelly, %s комиссии - Токены - Забронировать - Оплатите его криптой и сэкономьте **50 долларов** через нашего партнера Travala: **%1s - %2s** - Забронируйте отпуск с Tangem - Добавить - Изменить - Рыночная капитализация - Основная сеть - Не основной или основной блокчейн, на котором размещен токен - Не основные сети - Выберите сети - Кошелек - Не удалось найти этот токен, вы можете добавить его вручную. - - %1$d из %2$d кошелька - %1$d из %2$d кошельков - %1$d из %2$d кошельков - %1$d из %2$d кошельков - - например Bitcoin - Выбранный токен не доступен в кошельке на данный момент. Но не переживайте, вы можете выразить свой интерес проголосовав за его добавление. - Голосовать - Выберите кошелек - Кошелёк не поддерживает более одной сети - Вам необходимо установить единый код доступа для защиты всех ваших карт - Защита - Позже вы сможете установить индивидуальный код доступа для каждой карты - Персонализация - Код доступа можно восстановить с помощью привязанной карты. Не храните все карты в одном месте. - Восстановление - Выберите любое слово, фразу или число в качестве кода доступа - Создайте код доступа - Введите код доступа еще раз, чтобы избежать ошибки - Повторно введите код доступа - Код доступа должен состоять не менее чем из 4 символов. - Введенные коды доступа не совпадают - Необходимо повторить операцию, при этом карта будет сброшена к заводским настройкам - Ошибка активации - Добавление токенов - Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить? - Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас. - Парольная фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов. - Добавить резервную карту - Сканировать карту #%d - Создать резервную копию - Сканировать основную карту - Перейти к моему кошельку - Завершение бэкапа - Получить криптовалюту - Сканировать основную карту - Пропустить - Как это работает? - Давайте сгенерируем все ключи на вашей карте и создадим безопасный кошелек - Создать кошелек - Создать кошелек - Другие опции - Ваши ключи будут надежно сгенерированы внутри карты. Никакой seed-фразы, а это значит, что никто не может экспортировать или украсть ее. - Cоздавайте ключи приватно - Ваша карта активирована и готова к использованию - Успешно! - В этом случае вам будет необходимо начать процесс заново. - Вы хотите выйти из процесса активации? - Подготовка - Другой кошелек уже был создан на карте, которую вы пытаетесь добавить. Если на нем есть средства, пожалуйста сначала выведите их, а затем сделайте сброс до заводских настроек и используйте как резервную. - Резервная копия - Прочитать о seed-фразе - - - Запишите эти %d слова в порядке, указанном ниже, и сохраните их в надежном месте. - Запишите эти %d слов в порядке, указанном ниже, и сохраните их в надежном месте. - Запишите эти %d слов в порядке, указанном ниже, и сохраните их в надежном месте. - - Ваша seed-фраза - - - %d слова - %d слов - %d слов - - Чтобы импортировать кошелек, введите seed-фразу в поле ниже - Создать seed-фразу - Импорт кошелька - Seed-фраза — это набор слов, который дает возможность восстановить кошелек. В отличие от ключей, сгенерированных картой, seed-фраза не защищена и может быть скопирована и украдена. Используйте этот вариант на свой страх и риск. - Использовать seed-фразу - Неверная seed-фраза. Пожалуйста, проверьте порядок слов. - Неверная seed-фраза. Пожалуйста, проверьте орфографию. - Устаревший - Чтобы проверить, правильно ли вы записали seed-фразу, введите 2-е, 7-е и 11-е слова - Итак, проверим - Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты. - Вы можете добавить еще одну карту или завершить процесс резервного копирования - Подготовьте резервную карту с номером %s - Отсканируйте основную карту, чтобы начать процесс резервного копирования. - Подготовьте основную карту с номером %s - Ваша карта настроена и готова к использованию. - Добавлено максимальное количество карт. Завершите процесс резервного копирования. - Активация карты - Резервная карта #%d - Нет резервных карт - Добавлена ​​одна резервная карта - Подготовьте свою карту - Добавлены две резервные карты - Пополните кошелек на любую сумму, чтобы начать пользоваться картой - Пополните кошелек более чем на %1$s %2$s, чтобы начать пользоваться картой - Купить криптовалюту - Показать адрес кошелька - Активация кошелька - Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас. - Если процесc создания кошелька каким-либо образом прервется, вам придется начинать сначала - Вы можете сделать резервную копию своих ключей на одной или двух других пустых картах Wallet. - Код доступа можно восстановить с помощью одной из резервных карт. - Все резервные карты являются полнофункциональными и содержат одинаковые ключи. - Вы сможете установить код доступа для защиты своих кошельков. - Резервная копия карты - Восстановление кода доступа - Идентичные карты - Код доступа - Группы - По балансу - Сортировка токенов - Список - Выбрать из галереи - Настройки - Вы не предоставили доступ к вашей камере - Доступ к камере запрещен - %1$s (%2$s) в сети %3$s - Отправляйте только %s на этот адрес. Использование другой сети может привести к утрате средств. - Участвовать - Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. - Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже. - Грядущие выплаты - Ваши друзья купили - Меньше - Больше - Нет грядущих выплат - - за %d кошелек - за %d кошелька - за %d кошельков - за %d кошельков - - Получите ^^%1$s^^ на ваш адрес в сети %2$s %3$s ^^спустя 30 дней^^ за каждый кошелек, который купит ваш друг - Вы - Получит - при покупке кошелька на сайте tangem.com - %s скидку - Ваш друг - Персональный код скопирован! - Ваш персональный код - Купи Tangem Wallet со скидкой!\n%s - Приведи друга в Tangem - Вы приняли - Нажимая на эту кнопку, вы принимаете - в реферальной программе - - %d кошелек - %d кошелька - %d кошельков - %d кошельков - - Сбросить карту - Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку - Я понимаю, что не смогу этой картой восстановить пароль на остальных картах этого кошелька, если я его забуду - Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать данную карту для восстановления кода доступа. - Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек. - У вас есть карта банка другой страны, а также вид на жительство или регистрация вне РФ? - Карты банков РФ в данный момент не принимаются - Войдите в приложение и следите за своим балансом без сканирования карты - Доступ в приложение - Использовать биометрию - Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты - Код доступа - Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков - Включите биометрическую аутентификацию - Вы хотите использовать биометрию? - Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта - Сканировать - Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. - Приготовьте свою карту - Уже содержится во введенном адресе - Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. - Вы указали комиссию ниже рекомендуемой, это может привести к задержке исполнения вашей транзакции. Продолжить? - Причина: %1$s\nКод: %2$s - Транзакция не выполнена - Сумма - Вы можете установить комиссию за транзакцию, изменив значение в поле Satoshi per vByte. - Это стоимость, которую вы готовы заплатить за каждую единицу газа. Чем выше цена газа, тем быстрее ваша транзакция будет обработана. (Приоритетная комиссия включена) - Приоритетная комиссия - Комиссия, которую пользователь может заплатить майнерам или валидаторам за ускорение включения его транзакции в блок. - %1$s, %2$s - Адрес - Код назначения - Введите адрес - Адрес совпадает с адресом кошелька - Комиссия, которая будет взята за вашу транзакцию. Вы можете выставить своё собственное значение. - Недопустимый Tag. Он не будет добавлен в транзакцию. - Недопустимый Memo. Он не будет добавлен в транзакцию. - Tag - Memo - Включая комиссию - Низкая - Нормальная - Приоритетная - Проверьте своё интернет соединение - Информация о комиссии сети недоступна - Из - Лимит газа - Это максимальное количество газа, которое будет потрачено на выполнение транзакции или контракта. Лимит газа предотвращает неожиданные или неограниченные расходы при выполнении транзакции. - Цена газа - Это стоимость, которую вы готовы заплатить за каждую единицу газа. Чем выше цена газа, тем быстрее ваша транзакция будет обработана. - Всё - Максимальная сумма - Комиссия не превысит - Недопустимый Memo - Покрытие сетевой комиссии - Недостаточно средств для перевода, так как сумма комиссии и сумма перевода в совокупности больше имеющегося баланса - Недостаточно средств - Аккаунт будет удален из блокчейна, если баланс упадет ниже экзистенциального депозита. Пожалуйста, оставьте %s на балансе. - Экзистенциальный депозит - Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. - Установлена высокая комиссия - Ввиду особенности сети %1$s комиссия при переводе всего баланса выше. Для того, чтобы снизить комиссию Вы можете оставить %2$s. - Комиссия повышена - Включенная комиссия превышает сумму перевода, что приводит к отрицательному значению - Недопустимая сумма - Минимальная сумма отправки - %1$s. Пожалуйста, убедитесь, что остаток после отправки также не будет меньше %2$s. - Адрес получателя не активирован. \nПожалуйста, измените сумму отправки, чтобы продолжить. - Сумма отправки не может быть менее %s - Оставить %s - Уменьшить на %s - Уменьшить до %s - Обратите внимание, что при определенных параметрах комиссии возможны задержки по вашей транзакции - Возможны задержки по транзакции - Из-за ограничений %1$s в одну транзакцию может поместиться только %2$s UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму. - Лимит транзакции - Опционально - Пожалуйста, совместите свой QR-код с квадратом, чтобы отсканировать его. Убедитесь, что вы сканируете адрес в сети %s. - Последние - Получатель - Неверный адрес - Убедитесь, что вы отправляете средства на адрес кошелька %s. Ошибки могут привести к потере ваших токенов. - Отправить - Мемо/ Код назначения - это код, разделяющий транзакции к общему получателю в сети криптовалют. Внимание: отсутствие мемо может привести к потере средств. - Мои кошельки - Способ измерения комиссии за биткоин-транзакцию. Он указывает на количество самой маленькой единицы биткоина (сатоши) за каждый виртуальный байт в транзакции. Чем выше число, тем быстрее будет обработана транзакция майнерами. - Сатоши / вбайт - Отправка - Нажмите на любое поле, чтобы изменить его - Отправка %s - Вы отправляете **%1$s**, включая комиссию сети %2$s - Вы отправляете **%1$s** и %2$s - Отправка %s - Всего - %1$s и %2$s будет отправлено - ≈ %1$s (вкл. комиссию: %2$s) - %s будет отправлено - Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время - Неверный адрес - Транзакция отправлена - Забыть кошелек - Это приведет к удалению кошелька из приложения. Сам кошелек можно добавить снова. - Имя - Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. - Революционный аппаратный кошелек - До трех карт с одним кошельком - Все ключи в безопасности - Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте - Тысячи криптовалют - Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону. - Кошелек для каждого - Встречайте Tangem - Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах - Поддержка Web 3.0 - Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке. - Новый провайдер обмена! - В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя - В сумму включена комиссия провайдера сервиса. - Комиссии - Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. - Подтвердить - Вы отправляете - Дать разрешение - Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму. - Недостаточно средств - Подтвердить - Текущая транзакция - Комиссия сети за одобрение токена будет взиматься за подтверждение того, что именно вы разрешаете использовать ваш токен для обмена. - Дать разрешение - Укажите лимит доступа к выбранному токену - Количество %s - Чтобы продолжить, вам нужно разрешить смарт-контракту %1$s использовать ваш %2$s - Безлимитно - В процессе - Обменять - Вы получите - Выберите токен - не доступен - Балансы скрыты - Балансы показаны - Отменить - Выбранная операция в данный момент недоступна. Попробуйте позже. - В данный момент покупка монеты %s недоступна. Следите за нашими обновлениями. - У вас нет средств для продажи. Пополните счет, чтобы иметь возможность продать с него средства. - У вас нет средств для отправки. Пополните счет, чтобы иметь возможность отправить с него средства. - В данный момент обмен монеты %s недоступен. Следите за нашими обновлениями. - Продажа средств станет доступной после завершения транзакции(-ий) в сети %s - Отправка средств станет доступной после завершения транзакции(-ий) в сети %s - В данный момент продажа %s недоступна. Следите за нашими обновлениями. - Сгенерировать XPUB - Скрыть - Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами. - Скрыть %s - Скрыть токен - Стейкинг позволяет вам зарабатывать %1$s и получать вознаграждения каждые %2$s дней - Зарабатывайте до %s вознаграждений за стейкинг ежегодно - %1$s токен в сети %%image%% %2$s - Токен в сети %%image%% %1$s - Токен %1$s (%2$s) является основной валютой в сети %3$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети - Невозможно скрыть %s - Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля. - Обмен с Changelly, %s комиссии - Обменять - контракт: %s - У вас еще нет транзакций - Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. - Несколько адресов - История транзакций в настоящее время не поддерживается для этого блокчейна. Но не волнуйтесь, мы работаем над этим! А пока вы можете проверить ее в обозревателе. - Операция - от: %s - на: %s - Вы отсканировали ту же карту. Для создания twin-кошелька вам необходимо отсканировать карту с номером %d - Вы отсканировали не ту twin-карту. Пожалуйста, попробуйте отсканировать другую - Это карта, которую вы держите в руках. У парной карты номер %s.\n\nОбе карты можно использовать для вывода средств из этого кошелька. - Один кошелек. Две карты. - Сканировать карту #%s - Создание кошелька - Отсканируйте twin-карту #%s - Подготовка карты - Tangem Twin - Это действие необратимо. У вас не будет доступа к старому кошельку. - Приложите twin-карту с номером %s и не убирайте до окончания операции - Используйте %s или отсканируйте карту, чтобы получить доступ к своему кошельку - Добавить новый кошелек - Вы уверены, что хотите удалить этот кошелек? - Произошла ошибка, пожалуйста, отсканируйте свою карту для входа - Этот кошелек уже был сохранен, вы можете добавить другой - Кошелек с именем %s уже существует - Имя кошелька - Переименование кошелька - Разблокировать все - Разблокировать все с %s - Блокчейн недоступен. Попробуйте позже. - Отсканируйте карту - Запрос на подпись сообщения.\n\n%s - Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s - Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s - Детали транзакции:\nОт: %1$s\nК: %2$s\nСумма: %3$s - Буфер обмена содержит код WalletConnect. Использовать скопированное значение или отсканировать QR-код - Запрос на создание транзакции для %1$s\n%2$s\n\nСумма: %3$s\nКомиссия: %4$s\nВсего: %5$s\nБаланс: %6$s - Невозможно отправить транзакцию. Недостаточно средств. - Не удалось установить сессию WalletConnect. Пожалуйста, повторите попытку позже. - Не все токены добавлены в ваш список. Пожалуйста, добавьте их в начале, а потом попробуйте снова. Недостающие токены: \n - Не удалось подписать сообщение.\nПожалуйста, попробуйте еще раз - Не удалось установить сессию WalletConnect за отведённое время. Пожалуйста, повторите попытку позже. - Запрос на подключение через WalletConnect содержит неподдерживаемые блокчеины. Неподдерживаемые блокчеины:\n - Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. - Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. - Неверная карта выбрана в приложении Tangem - Не удалось создать транзакцию из данных Dapp. Код: %s - Произошла непредвиденная ошибка. Код ошибки: %d Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. - Нет открытых сессий WalletConnect - Упс. Нет сессий. - Не удалось создать пару WalletConnect: %1$s - Вставить из буфера обмена - Сообщение для %1$s:\n%2$s - Запрос на открытие сессии для\n%1$s\n\nСЕТЬ: %2$s\n\nURL: %3$s - Операция не может быть завершена.\n\nВы уже установили сеанс WalletConnect с этими параметрами. - Сканировать новый код - Эту карту нельзя использовать с WalletConnect. - Сеть не поддерживается. Пожалуйста, выберите другую сеть. - Выберите сеть - Сессии WalletConnect - Подключение к dApps - WalletConnect - Рыночная цена %s - за 24 часа - Сеть %s - Адрес скопирован в буфер обмена - Нет соединения с интернетом - Настройки кошелька - Tangem - Используйте %s или отсканируйте карту, чтобы разблокировать доступ к вашему кошельку - Пожалуйста, выведите все средства из этого кошелька, сбросьте его к заводским настройкам и создайте новый. Доступ к текущему кошельку будет утерян. - Ошибка активации - По решению разработчиков сети BNB стандарт BEP-2 перестанет поддерживаться в июне 2024 года. Чтобы не потерять активы, их необходимо преобразовать в стандарт BEP-20. Используйте функцию обмена в приложении или сторонние сервисы, чтобы перевести средства в cеть BNB Smart Chain. - Отключение сети BNB Beacon Chain - Можно лучше - Нравится - Понятно! - Очень круто! - Обновить - Вы находитесь в режиме демо - Демо режим включен - Отсканированная вами карта является картой разработчика. Не используйте ее для создания своего кошелька. - Не для пользователя! - Cеть %1$s использует концепцию экзистенциального депозита. Если баланс вашего счета будет ниже %2$s, то он будет деактивирован, а средства на счете уничтожены. - Для работы с сетью необходим депозит - Обмен будет доступен после завершения %s транзакции - У вас есть активная транзакция - Разрешение обмена в процессе и будет скоро завершено - Разрешение в процессе - Минимальная сумма обмена - %1$s. Пожалуйста, убедитесь, что остаток после обмена также не будет меньше %2$s. - У вас в списке нет монет доступных для обмена с %s - Нет доступных для обмена токенов - Чтобы совершить транзакцию, вам необходимо внести немного %1$s %2$s - Невозможно покрыть комиссию %s - Сумма получения не может быть менее %s - Cервис временно недоступен - Сумма для обмена должна быть не более %s - Сумма для обмена должна быть не менее %s - Пожалуйста, измените сумму для обмена - Возможно, данная карта - образец или подделка - Ошибка проверки подлинности - Ассоциировать - Этот токен должен быть ассоциирован с вашей учетной записью Hedera, прежде чем вы сможете его принять. Стоимость ассоциации ~%1$s %2$s - Этот токен должен быть ассоциирован с вашей учетной записью Hedera, прежде чем вы сможете его принять - Ассоциируете свой токен - Недостаточно %s. Пополните ваш аккаунт Hedera для ассоциации этого токена - На этой карте осталось всего %s подписей. Вам следует вывести все ваши средства. - Малое количество подписей - Токены на разных сетях могут иметь разные адреса. Пожалуйста, убедитесь при переводе средств, что ваш адрес соответствует сети. - - Используйте вашу карту, чтобы получить адрес для %d сети - Используйте вашу карту, чтобы получить адреса для %d сетей - Используйте вашу карту, чтобы получить адреса для %d сетей - Используйте вашу карту, чтобы получить адреса для %d сетей - - Некоторые адреса отсутствуют - В данный момент сеть недоступна. Пожалуйста, попробуйте позже. - Сеть недоступна - Пополните ваш кошелек - Ваш кошелек не имеет резервной копии. Проведите эту процедуру сейчас, чтобы защитить ваши активы. - Резервная копия отсутствует - Эта карта ранее использовалась для подписи транзакций. Если она получена от ненадежного источника, рассмотрите возможность вывода своих средств. Если это ваша карта, дополнительных действий не требуется. - Карта уже подписывала транзакции - Ваш отзыв мотивирует нас сделать кошелек Tangem еще лучше - Нравится Tangem? - Вам необходимо провести ассоциацию токена для того, чтобы иметь возможность принимать его - Необходима плата за аренду сети - %1$s - это монета в сети %2$s. Для совершения транзакции %3$s, вам необходимо внести немного %4$s (%5$s), чтобы покрыть комиссию сети. - Недостаточно %1$s для оплаты комиссии сети - Сеть Солана испытывает высокую нагрузку. Если Ваша транзакция не прошла в течение 2 минут, повторите её отправку. - Оповещение сети Солана - Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. - Некоторые сети в настоящее время недоступны. Пожалуйста, повторите попытку позже. - Некоторые сети недоступны - Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки. - Только для целей тестирования - Отказаться - Вы не закончили резервное копирование. Хотите продолжить? - Да, возобновить - Отказаться - Если сейчас отказаться, то придётся сбрасывать карты до заводских настроек, чтобы начать заново - Возобновить резервное копирование - Это необратимое действие - Войти с %s - Сканировать карту - Используйте %s или отсканируйте карту для входа в приложение - C возвращением! + Добавить токен + Валюты + Отправляйте только %1$s (%2$s) в сети %3$s на этот адрес. Использование другой сети может привести к утрате средств. + Обратиться в поддержку + Эта функция недоступна в демонстрационном режиме + Причина: %s + Не могу отправить транзакцию + Выбранный кошелёк не поддерживает сеть %1$s + Для активации криптографии сети %1$s необходимо сбросить кошелек до заводских настроек. Пожалуйста, выведите свои средства, чтобы не потерять их, после сброса доступ к текущему кошельку будет невозможен. + Токены в сети %1$s не поддерживаются этой картой из-за ограничений прошивки. + У вас возникли трудности со сканированием карты? + Эта карта не предназначена для работы с этим приложением + Подключите функцию комиссии по умолчанию и при формировании транзакции на отправку средств комиссия будет выставлена автоматически, а экран комиссии пропущен. Вы всегда сможете на него вернуться. + Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem + Включите биометрическую аутентификацию + Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. + При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения. + Сохранение кода доступа + Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. + Cохранение кошелька + Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. + Тёмная + Светлая + Как в системе + Тема + Настройки приложения + Чтобы скрыть или показать баланс, просто поверните ваше устройство вниз или отключите опцию его в разделе \"Настройки\" + Больше не показывать + Понятно + Балансы скрыты + Пожалуйста, отсканируйте карту + Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту + Слишком много попыток + Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. + Начать резервное копирование + + %d карта + %d карты + %d карт + %d карт + + Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. + Использовать эту карту для сброса кода доступа на других картах в этом кошельке + Восстановление кода доступа + Сбросить + Вы уверены, что хотите это сделать? + Смена кода доступа + Код доступа будет изменен только на данной карте + Все карты выбранного кошелька сброшены до заводских настроек, вы можете создать новый кошелек + Сброс завершён + Хотите сбросить следующую карту от этого кошелька? + Сброс карты + Рекомендуем завершить процесс сброса всех карт кошелька + Вы сбросили не все карты + Заводские настройки + Тип безопасности + Настройки карты + Помимо сетевой комиссий, сеть Cardano взимает %1$s ADA при транзакции с токеном %2$s + Требования к транзакции Cardano + Чтобы совершить транзакцию %1$s, внесите некоторую сумму ADA для покрытия сетевой комиссии и минимального значения ADA (рекомендуется 5 ADA) + Недостаточно ADA для транзакции + Вы должны поддерживать некоторое количество ADA, поскольку у вас на балансе есть токены в сети Cardano + Недостаточно ADA + Принять + Доступ запрещен + Применить + Одобрение + Внимание + Баланс: %s + Баланс + биометрическую аутентификацию + биометрией + Купить + Перейти на %1$s + Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности. + Отмена + Закрыть + Продолжить + Копировать + Скопировать адрес + Создать + Удалить + Отключено + Готово + Включить + Включено + Ошибка + Обозреватель + Посмотреть историю транзакций + Обозреватель + Комиссия + Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s + Свое + Быстро + По рынку + Медленно + Скорость и комиссия + Получить адреса + К провайдеру + Перейти в токен + Импортировать + Позже + Заблокирован + Основная сеть + Сетевая комиссия + Сумма отправки будет уменьшена на %1$s (%2$s) для покрытия выбранного уровня комиссии + Далее + Нет + Нет адреса + OK + Основная карта + Парольная фраза + Вставить + Подробнее + Получить + Отклонить + Перезагрузить + Переименовать + Сохранить изменения + Искать + Поиск токенов + Seed-фраза + Выберите действие + Продать + Отправить + Сервер недоступен, повторите попытку позднее + Поделиться + Подписать + Подписать и отправить + Начать + Отправить + Успешно + Поддержка + Обмен + условия участия + Ошибка транзакции + Транзакции + Перевод + Я понял + Произошла ошибка. Пожалуйста, попробуйте снова. + Недоступно + Да + Адрес контракта скопирован! + Доступные сети + Добавить токен + Адрес контракта + Адрес контракта некорректен + Пожалуйста, выберите сеть + Десятичное число должно быть действительным целым числом, до %li + Своя деривация + Например m/00\'/0000\'/0\'/0/0 + Введите свою деривацию + Знаков после запятой + Путь деривации + По умолчанию + Деривация по BIP44 + Введенный путь деривации некорректен + Например, USD Coin + Название токена + Не выбрано + Сеть + Сеть + Вы можете добавить токен в ручную, если он не поддерживается Tangem + Например, USDC + Символ + Символ токена + Этот токен/сеть уже находится в вашем списке + Токены могут быть созданы кем угодно. Остерегайтесь мошеннических токенов, они могут ничего не стоить + Остерегайтесь мошеннических токенов, они могут ничего не стоить + Токены могут быть созданы кем угодно + Чат + Код доступа + Перед сканированием карты вам нужно будет ввести правильный код доступа. + Задержка сканирования + Этот механизм защищает карту от бесконтактных атак. Между сканированием карты и выполнением команды будет добавлена задержка. + Пароль + Перед выполнением любой команды, влекущей за собой изменение состояния карты, вам необходимо будет ввести пароль. + Реферальная программа + Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы + %s хэшей + Номер карты + Обратиться в поддержку + Добавить еще карты + Валюта приложения + Скрывать балансы жестом переворота + Эмитент + Подписано + Подробности + Проверьте подключение с интернетом или переключитесь на другую сеть + Условия использования + Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. + Мои токены + У вас нет добавленных токенов. Добавьте токены для обмена + Недоступен для обмена с %s + Предоставлено + Статус + Tangem предоставляет доступ к обмену через сторонних поставщиков в соответствии с их правилами + Выберите провайдера + Произошла ошибка. Код: %s + К сожалению, обмен указанной пары через выбранного провайдера на данный момент невозможен. Попробуйте совершить обмен позже. (Код: %s) + Выбранный провайдер недоступен для обмена. Попробуйте позже. (Код: %s) + В данный момент обмен невозможен. Попробуйте позже. (Код: %s) + Курс обмена + Обмен через %s + Чтобы вернуть ваши деньги, посетите сайт провайдера + Операция не выполнена провайдером + Отправленные средства были возвращены в %1$s на ваш кошелек в соответствии с правилами OKX или моста обмена. %2$s + Сумма была возвращена в %1$s (%2$s сети) + Посетите сайт провайдера для проверки + Провайдер запрашивает прохождение верификации + Отменен + Подтверждено + Подтверждение + Подтверждение... + Обменяно + Обмен + Обмен... + Неудачно + Депозит получен + Ожидание депозита + Ожидаем пополнения... + Возвращено + Отправляем + Отправка средств... + Отправлено + Данные провайдера. Сумма к получению может измениться в зависимости от рыночных условий. + Статус обмена + Требуется верификация + Ожидание хеша транзакции + Список токенов в вашем кошельке + Получение наилучших курсов... + Плавающая ставка + Пользуясь сервисом, вы соглашаетесь с %s + Пользуясь сервисом, вы соглашаетесь с %1$s и %2$s + Больше провайдеров на подходе.\nСледите за обновлениями! + Политикой конфиденциальности + Провайдер + Лучший курс + Доступно до %s + Доступно с %s + Недоступно для этой пары + Требуется разрешение + Рекомендовано + Условиями использования + Токены не найдены. Пожалуйста, попробуйте другой запрос + ID: %s + ID транзакции скопирован + Информация ниже не является обязательной. Вы можете стереть её, если хотите. + Расскажите, каких функций вам не хватает, и мы постараемся вам помочь. + Скажите, пожалуйста, какая у вас карта? + Привет, команда поддержки, + Пожалуйста, расскажите нам больше о вашей проблеме. Каждая маленькая деталь может помочь. + Мои предложения + Не могу отсканировать карту + Обращение в поддержку + Обращение в поддержку Tangem + Не могу отправить транзакцию + Купить + Сканировать + Чтобы изменить код доступа, приложите карту как показано выше и не убирайте до окончания операции + Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции + Чтобы создать кошелек, приложите карту как показано выше и не убирайте до окончания операции + Приложите карту #%s для сброса + Приложите, чтобы отсканировать + Приложите, чтобы подписать + Приложите карту + Вы обновили данные биометрии, отсканируйте свою карту для входа + Ваш баланс должен быть выше суммы комиссии для осуществления перевода + Недостаточно средств + У вас недостаточно Маны для этой транзакции. Пожалуйста, подождите, пока Мана восполнится. Ваш баланс маны равен %1$s/%2$s + Недостаточно Маны + Вы можете перевести только %s из-за ограничения Mana, установленного сетью Koinos + Лимит маны + Сеть Koinos использует Ману для оплаты комиссии сети. У вас есть %1$s/%2$s Mana + Уровень маны + Чтобы начать отслеживать свои криптоактивы и транзакции, добавьте токены + Управление токенами + Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту + Отсканируйте карту + Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля. + Обмен с Changelly, %s комиссии + Токены + Забронировать + Оплатите его криптой и сэкономьте **50 долларов** через нашего партнера Travala: **%1s - %2s** + Забронируйте отпуск с Tangem + Добавить + Изменить + Рыночная капитализация + Основная сеть + Не основной или основной блокчейн, на котором размещен токен + Не основные сети + Выберите сети + Кошелек + Не удалось найти этот токен, вы можете добавить его вручную. + + %1$d из %2$d кошелька + %1$d из %2$d кошельков + %1$d из %2$d кошельков + %1$d из %2$d кошельков + + например Bitcoin + Выбранный токен не доступен в кошельке на данный момент. Но не переживайте, вы можете выразить свой интерес проголосовав за его добавление. + Голосовать + Выберите кошелек + Кошелёк не поддерживает более одной сети + Вам необходимо установить единый код доступа для защиты всех ваших карт + Защита + Позже вы сможете установить индивидуальный код доступа для каждой карты + Персонализация + Код доступа можно восстановить с помощью привязанной карты. Не храните все карты в одном месте. + Восстановление + Выберите любое слово, фразу или число в качестве кода доступа + Создайте код доступа + Введите код доступа еще раз, чтобы избежать ошибки + Повторно введите код доступа + Код доступа должен состоять не менее чем из 4 символов. + Введенные коды доступа не совпадают + Необходимо повторить операцию, при этом карта будет сброшена к заводским настройкам + Ошибка активации + Добавление токенов + Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить? + Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас. + Парольная фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов. + Добавить резервную карту + Сканировать карту #%d + Создать резервную копию + Сканировать основную карту + Перейти к моему кошельку + Завершение бэкапа + Получить криптовалюту + Сканировать основную карту + Пропустить + Как это работает? + Давайте сгенерируем все ключи на вашей карте и создадим безопасный кошелек + Создать кошелек + Создать кошелек + Другие опции + Ваши ключи будут надежно сгенерированы внутри карты. Никакой seed-фразы, а это значит, что никто не может экспортировать или украсть ее. + Cоздавайте ключи приватно + Ваша карта активирована и готова к использованию + Успешно! + В этом случае вам будет необходимо начать процесс заново. + Вы хотите выйти из процесса активации? + Подготовка + Другой кошелек уже был создан на карте, которую вы пытаетесь добавить. Если на нем есть средства, пожалуйста сначала выведите их, а затем сделайте сброс до заводских настроек и используйте как резервную. + Резервная копия + Прочитать о seed-фразе + + + Запишите эти %d слова в порядке, указанном ниже, и сохраните их в надежном месте. + Запишите эти %d слов в порядке, указанном ниже, и сохраните их в надежном месте. + Запишите эти %d слов в порядке, указанном ниже, и сохраните их в надежном месте. + + Ваша seed-фраза + + + %d слова + %d слов + %d слов + + Чтобы импортировать кошелек, введите seed-фразу в поле ниже + Создать seed-фразу + Импорт кошелька + Seed-фраза — это набор слов, который дает возможность восстановить кошелек. В отличие от ключей, сгенерированных картой, seed-фраза не защищена и может быть скопирована и украдена. Используйте этот вариант на свой страх и риск. + Использовать seed-фразу + Неверная seed-фраза. Пожалуйста, проверьте порядок слов. + Неверная seed-фраза. Пожалуйста, проверьте орфографию. + Устаревший + Чтобы проверить, правильно ли вы записали seed-фразу, введите 2-е, 7-е и 11-е слова + Итак, проверим + Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты. + Вы можете добавить еще одну карту или завершить процесс резервного копирования + Подготовьте резервную карту с номером %s + Отсканируйте основную карту, чтобы начать процесс резервного копирования. + Подготовьте основную карту с номером %s + Ваша карта настроена и готова к использованию. + Добавлено максимальное количество карт. Завершите процесс резервного копирования. + Активация карты + Резервная карта #%d + Нет резервных карт + Добавлена ​​одна резервная карта + Подготовьте свою карту + Добавлены две резервные карты + Пополните кошелек на любую сумму, чтобы начать пользоваться картой + Пополните кошелек более чем на %1$s %2$s, чтобы начать пользоваться картой + Купить криптовалюту + Показать адрес кошелька + Активация кошелька + Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас. + Если процесc создания кошелька каким-либо образом прервется, вам придется начинать сначала + Вы можете сделать резервную копию своих ключей на одной или двух других пустых картах Wallet. + Код доступа можно восстановить с помощью одной из резервных карт. + Все резервные карты являются полнофункциональными и содержат одинаковые ключи. + Вы сможете установить код доступа для защиты своих кошельков. + Резервная копия карты + Восстановление кода доступа + Идентичные карты + Код доступа + Группы + По балансу + Сортировка токенов + Список + Выбрать из галереи + Настройки + Вы не предоставили доступ к вашей камере + Доступ к камере запрещен + %1$s (%2$s) в сети %3$s + Отправляйте только %s на этот адрес. Использование другой сети может привести к утрате средств. + Участвовать + Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. + Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже. + Грядущие выплаты + Ваши друзья купили + Меньше + Больше + Нет грядущих выплат + + за %d кошелек + за %d кошелька + за %d кошельков + за %d кошельков + + Получите ^^%1$s^^ на ваш адрес в сети %2$s %3$s ^^спустя 30 дней^^ за каждый кошелек, который купит ваш друг + Вы + Получит + при покупке кошелька на сайте tangem.com + %s скидку + Ваш друг + Персональный код скопирован! + Ваш персональный код + Купи Tangem Wallet со скидкой!\n%s + Приведи друга в Tangem + Вы приняли + Нажимая на эту кнопку, вы принимаете + в реферальной программе + + %d кошелек + %d кошелька + %d кошельков + %d кошельков + + Сбросить карту + Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку + Я понимаю, что не смогу этой картой восстановить пароль на остальных картах этого кошелька, если я его забуду + Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать данную карту для восстановления кода доступа. + Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек. + У вас есть карта банка другой страны, а также вид на жительство или регистрация вне РФ? + Карты банков РФ в данный момент не принимаются + Войдите в приложение и следите за своим балансом без сканирования карты + Доступ в приложение + Использовать биометрию + Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты + Код доступа + Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков + Включите биометрическую аутентификацию + Вы хотите использовать биометрию? + Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта + Сканировать + Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. + Приготовьте свою карту + Уже содержится во введенном адресе + Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. + Вы указали комиссию ниже рекомендуемой, это может привести к задержке исполнения вашей транзакции. Продолжить? + Причина: %1$s\nКод: %2$s + Транзакция не выполнена + Сумма + Вы можете установить комиссию за транзакцию, изменив значение в поле Satoshi per vByte. + Это стоимость, которую вы готовы заплатить за каждую единицу газа. Чем выше цена газа, тем быстрее ваша транзакция будет обработана. (Приоритетная комиссия включена) + Приоритетная комиссия + Комиссия, которую пользователь может заплатить майнерам или валидаторам за ускорение включения его транзакции в блок. + %1$s, %2$s + Адрес + Код назначения + Введите адрес + Адрес совпадает с адресом кошелька + Комиссия, которая будет взята за вашу транзакцию. Вы можете выставить своё собственное значение. + Недопустимый Tag. Он не будет добавлен в транзакцию. + Недопустимый Memo. Он не будет добавлен в транзакцию. + Tag + Memo + Включая комиссию + Низкая + Нормальная + Приоритетная + Проверьте своё интернет соединение + Информация о комиссии сети недоступна + Из + Лимит газа + Это максимальное количество газа, которое будет потрачено на выполнение транзакции или контракта. Лимит газа предотвращает неожиданные или неограниченные расходы при выполнении транзакции. + Цена газа + Это стоимость, которую вы готовы заплатить за каждую единицу газа. Чем выше цена газа, тем быстрее ваша транзакция будет обработана. + Всё + Максимальная сумма + Комиссия не превысит + Недопустимый Memo + Покрытие сетевой комиссии + Недостаточно средств для перевода, так как сумма комиссии и сумма перевода в совокупности больше имеющегося баланса + Недостаточно средств + Аккаунт будет удален из блокчейна, если баланс упадет ниже экзистенциального депозита. Пожалуйста, оставьте %s на балансе. + Экзистенциальный депозит + Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. + Установлена высокая комиссия + Ввиду особенности сети %1$s комиссия при переводе всего баланса выше. Для того, чтобы снизить комиссию Вы можете оставить %2$s. + Комиссия повышена + Включенная комиссия превышает сумму перевода, что приводит к отрицательному значению + Недопустимая сумма + Минимальная сумма отправки - %1$s. Пожалуйста, убедитесь, что остаток после отправки также не будет меньше %2$s. + Адрес получателя не активирован. \nПожалуйста, измените сумму отправки, чтобы продолжить. + Сумма отправки не может быть менее %s + Оставить %s + Уменьшить на %s + Уменьшить до %s + Обратите внимание, что при определенных параметрах комиссии возможны задержки по вашей транзакции + Возможны задержки по транзакции + Из-за ограничений %1$s в одну транзакцию может поместиться только %2$s UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму. + Лимит транзакции + Опционально + Пожалуйста, совместите свой QR-код с квадратом, чтобы отсканировать его. Убедитесь, что вы сканируете адрес в сети %s. + Последние + Получатель + Неверный адрес + Убедитесь, что вы отправляете средства на адрес кошелька %s. Ошибки могут привести к потере ваших токенов. + Отправить + Мемо/ Код назначения - это код, разделяющий транзакции к общему получателю в сети криптовалют. Внимание: отсутствие мемо может привести к потере средств. + Мои кошельки + Способ измерения комиссии за биткоин-транзакцию. Он указывает на количество самой маленькой единицы биткоина (сатоши) за каждый виртуальный байт в транзакции. Чем выше число, тем быстрее будет обработана транзакция майнерами. + Сатоши / вбайт + Отправка + Нажмите на любое поле, чтобы изменить его + Отправка %s + Вы отправляете **%1$s**, включая комиссию сети %2$s + Вы отправляете **%1$s** и %2$s + Отправка %s + Всего + %1$s и %2$s будет отправлено + ≈ %1$s (вкл. комиссию: %2$s) + %s будет отправлено + Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время + Неверный адрес + Транзакция отправлена + Забыть кошелек + Это приведет к удалению кошелька из приложения. Сам кошелек можно добавить снова. + Имя + Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. + Революционный аппаратный кошелек + До трех карт с одним кошельком + Все ключи в безопасности + Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте + Тысячи криптовалют + Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону. + Кошелек для каждого + Встречайте Tangem + Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах + Поддержка Web 3.0 + Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке. + Новый провайдер обмена! + В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя + В сумму включена комиссия провайдера сервиса. + Комиссии + Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. + Подтвердить + Вы отправляете + Дать разрешение + Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму. + Недостаточно средств + Подтвердить + Текущая транзакция + Комиссия сети за одобрение токена будет взиматься за подтверждение того, что именно вы разрешаете использовать ваш токен для обмена. + Дать разрешение + Укажите лимит доступа к выбранному токену + Количество %s + Чтобы продолжить, вам нужно разрешить смарт-контракту %1$s использовать ваш %2$s + Безлимитно + В процессе + Обменять + Вы получите + Выберите токен + не доступен + Балансы скрыты + Балансы показаны + Отменить + Выбранная операция в данный момент недоступна. Попробуйте позже. + В данный момент покупка монеты %s недоступна. Следите за нашими обновлениями. + У вас нет средств для продажи. Пополните счет, чтобы иметь возможность продать с него средства. + У вас нет средств для отправки. Пополните счет, чтобы иметь возможность отправить с него средства. + В данный момент обмен монеты %s недоступен. Следите за нашими обновлениями. + Продажа средств станет доступной после завершения транзакции(-ий) в сети %s + Отправка средств станет доступной после завершения транзакции(-ий) в сети %s + В данный момент продажа %s недоступна. Следите за нашими обновлениями. + Сгенерировать XPUB + Скрыть + Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами. + Скрыть %s + Скрыть токен + Стейкинг позволяет вам зарабатывать %1$s и получать вознаграждения каждые %2$s дней + Зарабатывайте до %s вознаграждений за стейкинг ежегодно + %1$s токен в сети %%image%% %2$s + Токен в сети %%image%% %1$s + Токен %1$s (%2$s) является основной валютой в сети %3$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети + Невозможно скрыть %s + Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля. + Обмен с Changelly, %s комиссии + Обменять + контракт: %s + У вас еще нет транзакций + Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. + Несколько адресов + История транзакций в настоящее время не поддерживается для этого блокчейна. Но не волнуйтесь, мы работаем над этим! А пока вы можете проверить ее в обозревателе. + Операция + от: %s + на: %s + Вы отсканировали ту же карту. Для создания twin-кошелька вам необходимо отсканировать карту с номером %d + Вы отсканировали не ту twin-карту. Пожалуйста, попробуйте отсканировать другую + Это карта, которую вы держите в руках. У парной карты номер %s.\n\nОбе карты можно использовать для вывода средств из этого кошелька. + Один кошелек. Две карты. + Сканировать карту #%s + Создание кошелька + Отсканируйте twin-карту #%s + Подготовка карты + Tangem Twin + Это действие необратимо. У вас не будет доступа к старому кошельку. + Приложите twin-карту с номером %s и не убирайте до окончания операции + Используйте %s или отсканируйте карту, чтобы получить доступ к своему кошельку + Добавить новый кошелек + Вы уверены, что хотите удалить этот кошелек? + Произошла ошибка, пожалуйста, отсканируйте свою карту для входа + Этот кошелек уже был сохранен, вы можете добавить другой + Кошелек с именем %s уже существует + Имя кошелька + Переименование кошелька + Разблокировать все + Разблокировать все с %s + Блокчейн недоступен. Попробуйте позже. + Отсканируйте карту + Запрос на подпись сообщения.\n\n%s + Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s + Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s + Детали транзакции:\nОт: %1$s\nК: %2$s\nСумма: %3$s + Буфер обмена содержит код WalletConnect. Использовать скопированное значение или отсканировать QR-код + Запрос на создание транзакции для %1$s\n%2$s\n\nСумма: %3$s\nКомиссия: %4$s\nВсего: %5$s\nБаланс: %6$s + Невозможно отправить транзакцию. Недостаточно средств. + Не удалось установить сессию WalletConnect. Пожалуйста, повторите попытку позже. + Не все токены добавлены в ваш список. Пожалуйста, добавьте их в начале, а потом попробуйте снова. Недостающие токены: \n + Не удалось подписать сообщение.\nПожалуйста, попробуйте еще раз + Не удалось установить сессию WalletConnect за отведённое время. Пожалуйста, повторите попытку позже. + Запрос на подключение через WalletConnect содержит неподдерживаемые блокчеины. Неподдерживаемые блокчеины:\n + Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. + Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. + Неверная карта выбрана в приложении Tangem + Не удалось создать транзакцию из данных Dapp. Код: %s + Произошла непредвиденная ошибка. Код ошибки: %d Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. + Нет открытых сессий WalletConnect + Упс. Нет сессий. + Не удалось создать пару WalletConnect: %1$s + Вставить из буфера обмена + Сообщение для %1$s:\n%2$s + Запрос на открытие сессии для\n%1$s\n\nСЕТЬ: %2$s\n\nURL: %3$s + Операция не может быть завершена.\n\nВы уже установили сеанс WalletConnect с этими параметрами. + Сканировать новый код + Эту карту нельзя использовать с WalletConnect. + Сеть не поддерживается. Пожалуйста, выберите другую сеть. + Выберите сеть + Сессии WalletConnect + Подключение к dApps + WalletConnect + Рыночная цена %s + за 24 часа + Сеть %s + Адрес скопирован в буфер обмена + Нет соединения с интернетом + Настройки кошелька + Tangem + Используйте %s или отсканируйте карту, чтобы разблокировать доступ к вашему кошельку + Пожалуйста, выведите все средства из этого кошелька, сбросьте его к заводским настройкам и создайте новый. Доступ к текущему кошельку будет утерян. + Ошибка активации + По решению разработчиков сети BNB стандарт BEP-2 перестанет поддерживаться в июне 2024 года. Чтобы не потерять активы, их необходимо преобразовать в стандарт BEP-20. Используйте функцию обмена в приложении или сторонние сервисы, чтобы перевести средства в cеть BNB Smart Chain. + Отключение сети BNB Beacon Chain + Можно лучше + Нравится + Понятно! + Очень круто! + Обновить + Вы находитесь в режиме демо + Демо режим включен + Отсканированная вами карта является картой разработчика. Не используйте ее для создания своего кошелька. + Не для пользователя! + Cеть %1$s использует концепцию экзистенциального депозита. Если баланс вашего счета будет ниже %2$s, то он будет деактивирован, а средства на счете уничтожены. + Для работы с сетью необходим депозит + Обмен будет доступен после завершения %s транзакции + У вас есть активная транзакция + Разрешение обмена в процессе и будет скоро завершено + Разрешение в процессе + Минимальная сумма обмена - %1$s. Пожалуйста, убедитесь, что остаток после обмена также не будет меньше %2$s. + У вас в списке нет монет доступных для обмена с %s + Нет доступных для обмена токенов + Чтобы совершить транзакцию, вам необходимо внести немного %1$s %2$s + Невозможно покрыть комиссию %s + Сумма получения не может быть менее %s + Cервис временно недоступен + Сумма для обмена должна быть не более %s + Сумма для обмена должна быть не менее %s + Пожалуйста, измените сумму для обмена + Возможно, данная карта - образец или подделка + Ошибка проверки подлинности + Ассоциировать + Этот токен должен быть ассоциирован с вашей учетной записью Hedera, прежде чем вы сможете его принять. Стоимость ассоциации ~%1$s %2$s + Этот токен должен быть ассоциирован с вашей учетной записью Hedera, прежде чем вы сможете его принять + Ассоциируете свой токен + Недостаточно %s. Пополните ваш аккаунт Hedera для ассоциации этого токена + На этой карте осталось всего %s подписей. Вам следует вывести все ваши средства. + Малое количество подписей + Токены на разных сетях могут иметь разные адреса. Пожалуйста, убедитесь при переводе средств, что ваш адрес соответствует сети. + + Используйте вашу карту, чтобы получить адрес для %d сети + Используйте вашу карту, чтобы получить адреса для %d сетей + Используйте вашу карту, чтобы получить адреса для %d сетей + Используйте вашу карту, чтобы получить адреса для %d сетей + + Некоторые адреса отсутствуют + В данный момент сеть недоступна. Пожалуйста, попробуйте позже. + Сеть недоступна + Пополните ваш кошелек + Ваш кошелек не имеет резервной копии. Проведите эту процедуру сейчас, чтобы защитить ваши активы. + Резервная копия отсутствует + Эта карта ранее использовалась для подписи транзакций. Если она получена от ненадежного источника, рассмотрите возможность вывода своих средств. Если это ваша карта, дополнительных действий не требуется. + Карта уже подписывала транзакции + Ваш отзыв мотивирует нас сделать кошелек Tangem еще лучше + Нравится Tangem? + Вам необходимо провести ассоциацию токена для того, чтобы иметь возможность принимать его + Необходима плата за аренду сети + %1$s - это монета в сети %2$s. Для совершения транзакции %3$s, вам необходимо внести немного %4$s (%5$s), чтобы покрыть комиссию сети. + Недостаточно %1$s для оплаты комиссии сети + Сеть Солана испытывает высокую нагрузку. Если Ваша транзакция не прошла в течение 2 минут, повторите её отправку. + Оповещение сети Солана + Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. + Некоторые сети в настоящее время недоступны. Пожалуйста, повторите попытку позже. + Некоторые сети недоступны + Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки. + Только для целей тестирования + Отказаться + Вы не закончили резервное копирование. Хотите продолжить? + Да, возобновить + Отказаться + Если сейчас отказаться, то придётся сбрасывать карты до заводских настроек, чтобы начать заново + Возобновить резервное копирование + Это необратимое действие + Войти с %s + Сканировать карту + Используйте %s или отсканируйте карту для входа в приложение + C возвращением! diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index ffdb3de5f8..28ae458dd2 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1,741 +1,744 @@ - Add custom token - Manage tokens - Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds. - Request support - This feature is disabled in Demo mode - Reason: %s - Can\'t send a transaction - The selected does not support the %1$s network - To activate the %1$s blockchain\'s cryptographic encryption, you\'ll need to reset the wallet to factory settings. Please withdraw your funds before doing so to ensure that you don\'t lose them, and then complete the reset process. Access to the current wallet will not be possible after the reset. - Tokens in %1$s network are not supported by this card due to firmware limitation. - Are you having difficulty scanning your card? - This card is not designed to work with this app - Default Fee - Enable Default Fee to set transaction fees automatically and skip the Fee page when sending funds. You can always go back to this page if necessary. - Go to settings to enable biometric authentication in the Tangem App - Enable biometric authentication - This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - Removing the saved card deletes all the saved wallets and their access codes from the app. - Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. - Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. - Dark - Light - System default - Theme - App settings - To hide or show your balances, simply flip your device screen down, or switch it off in Settings - Don\'t show again - Got it - Balances are hidden - Please scan the card - Please try again in 30 seconds or scan the card - Too many attempts - You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. - Start backup process - - %d card - %d cards - - Disable this option if you don\'t want this card to be used to reset access codes on other cards in this wallet. Please note that this will also prevent you from resetting the access code on this card. - Allows you to use this card to reset access code on other cards in this wallet - Access code recovery - Reset - Are you sure you want to do this? - Change Access Code - Access code will be changed on this card only - All cards in the selected wallet have been reset to factory settings. You can now create a new wallet. - Reset complete - Do you want to reset the next card in this wallet? - Card reset - We recommend completing the reset process for all cards in this wallet - You haven\'t reset all your cards - Reset to Factory Settings - Security Mode - Card settings - In addition to network fee, the Cardano network charges %1$s ADA when transacting with the %2$s token - Cardano transaction requirements - To make a %1$s transaction, you must deposit some ADA to cover the network fee and minimum ADA value (5 ADA recommended) - Insufficient ADA for token transfer - You must maintain some ADA because you have some tokens on the Cardano blockchain - Not enough ADA - Accept - Access denied - Apply - Approval - Attention - Balance: %s - Balance - biometric authentication - biometrics - Buy - Go to %1$s - You have not given access to your camera, please adjust your privacy settings - Cancel - Close - Continue - Copy - Copy address - Create - Delete - Disabled - Done - Enable - Enabled - Error - Explore - Explore transaction history - Explorer - Fee - Network fees are charges users pay to process and confirm transactions. The fee amount can be affected by network congestion, transaction size, and execution priority. %s - Custom - Fast - Market - Slow - Speed and fee - Get addresses - Go to provider - Import - Later - Locked - Main network - Network fee - Amount sent will be reduced by %1$s (%2$s) to cover the selected fee level - Next - No - No address - OK - Primary Card - Passphrase - Paste - Read more - Receive - Reject - Reload - Rename - Save changes - Search - Search tokens - Seed phrase - Select action - Sell - Send - The server is not available, please try again later - Share - Sign - Sign and send - Start - Submit - Success - Support - Swap - terms and conditions - Transaction failed - Transactions - Transfer - I understand - There was an error. Please try again. - Unreachable - Yes - Contract address copied! - Available networks - Add token - Contract address - Contract address is invalid - Please select the network - Decimal must be a valid integer, up to %li - Custom derivation - E. g. m/00\'/0000\'/0\'/0/0 - Enter custom derivation - Decimals - Derivation Path - Default - BIP44 coin type - The derivation path you\'ve entered is not valid - E.g. USD Coin - Name - Not selected - Network - Token network - You can manually add a token that is not natively supported by Tangem - E.g. USDC - Symbol - Token symbol - This token/network has already been added to your list - Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. - Be aware of adding scam tokens, they can cost nothing - Note that tokens can be created by anyone - Chat - Access code - You will have to submit the correct access code before scanning the card - Long Tap - This mechanism protects against proximity attacks on a card. It will enforce a delay between reception and execution of a command. - Passcode - Before executing any command entailing a change of the card state, you will have to enter the passcode. - Referral program - Flip your device screen down to quickly hide and show balances - %s hashes - Card ID - Contact support - Link More Cards - App Currency - Flip-to-Hide Balances - Issuer - Signed - Details - Check your internet connection or switch to a different network - Terms of service - You have used a card from another wallet. Tap the card associated with this wallet - My tokens - You haven\'t added any tokens yet. Add tokens via Market to swap - Cannot be swapped for %s - Provided by - Status - Tangem offers token swaps via 3rd-party providers according to each provider\'s terms - Choose provider - An error occurred. Code: %s - Oops! Swapping the selected pair through the chosen provider is temporarily unavailable. Please try again later. (Code: %s) - Selected provider is unavailable at the moment. Please try again later. (Code: %s) - Swaps are unavailable at the moment. Please try again later. (Code: %s) - Estimated amount - Exchange by %s - Visit provider’s website to refund your money - Operation failed by provider - Visit provider’s website for verification - KYC verification required by provider - Canceled - Confirmed - Confirming - Confirming... - Exchanged - Exchanging - Exchanging... - Failed - Deposit received - Awaiting deposit - Awaiting deposit... - Refunded - Sending to you - Sending to you... - Sent - Provider-sourced data. Estimated amount subject to change due to market conditions. - Exchange status - Verification required - Awaiting transaction hash - List of all tokens added to your wallet - Fetching best rates... - Floating rate - By using swap functionality, you agree with provider’s %s - By using swap functionality, you agree with provider’s %1$s and %2$s - More providers are coming soon.\nStay tuned! - Privacy Policy - Provider - Best rate - Available up to %s - Available from %s - Unavailable for this pair - Permission Required - Recommended - Terms of Use - No tokens found. Please try another request - ID: %s - Transaction ID copied - The following information is optional. You can erase it if you don\'t want to share it. - Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have - Hi support team, - Please tell us more about your issue. Every small detail can help. - My suggestions - Can\'t scan a card - Feedback - Tangem feedback - Can\'t send a transaction - Order card - Scan card - To change the access code tap the card as shown above and do not remove until the end of the operation - To change the passcode tap the card as shown above and do not remove until the end of the operation - To create the wallet tap the card as shown above and do not remove until the end of the operation - Tap the card #%s of the wallet - Tap to scan - Tap to sign - Tap the card - You have updated biometrics, scan your card to enter - Your balance should be higher than the fee value to make a transfer - Not enough balance - You don\'t have enough Mana for this transaction. Please wait until the Mana is refilled. Your Mana balance is %1$s/%2$s - Not enough Mana - You can transfer only %s due to the Mana limit imposed by the Koinos network - Mana limit - The Koinos network requires Mana for network fees. Your have %1$s/%2$s Mana - Mana level - To begin tracking your crypto assets and transactions, add tokens - Manage tokens - To access all the networks you need to scan the card - Scan your card - Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s - Swap with Changelly, %s fees - Tokens - Book now - Save **$50** while booking via our partner Travala: **%1s - %2s** - Book your holidays with Tangem and pay in crypto - Add - Edit - Coin market cap - Blockchain the cryptocurrency was initially created - Native network - Using non-native networks for tokens enables cross-blockchain interoperability, allowing assets to be utilized in diverse decentralized applications and smart contracts across platforms. However, this often involves a custodian or smart contract to hold the original asset securely, introducing centralization and counterparty risk. - Not original or primary blockchain the token is hosted - Non-native networks - Choose networks - Wallet - Couldn’t find this token, you can add it manually - - %1$d of %2$d wallet - %1$d of %2$d wallets - - e.g. BTC I trust, hodl I must - The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it. - Upvote - Choose wallet - The wallet doesn\'t support more than one network - You have to set up a single access code to protect all your cards - Protect - You can set up an individual access code on each card later - Personalize - The access code can be restored with a linked card. Don’t keep all cards at one place. - Restore - Choose any word, phrase, or number you want as your access code - Create Access Code - Enter your access code one more time to avoid a mistake - Re-enter your Access Code - Access code must be at least 4 characters long - Entered access code didn\'t match the initial access code - Please repeat the operation. The card will be reset to factory settings. - Activation error - Add tokens - You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process? - The backup process is partly complete. You can\'t exit it now. - The passphrase is an advanced security feature that crypto wallets use. It adds an extra word or phrase of your own choosing to your already existing recovery phrase to unlock a brand-new set of addresses. - Add a backup card - Scan the card #%d - Backup now - Scan the primary card - Continue to my wallet - Finalize the backup - Receive crypto - Scan primary card - Skip for later - How does it work? - Let\'s generate all the keys on your card and create a secure wallet - Create wallet - Create a wallet - Other options - Your keys will be securely generated inside the card. There is no seed phrase, which means nobody can export or steal it. - Generate keys privately - Your card is activated and ready to be used - Success! - In this case, you will need to start from the beginning. - Do you want to exit the activation process? - Getting started - Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup. - Creating a backup - Read more about seed phrase - - - Write these %d words down in the order given below and store them in a safe and secret place. - - Your seed phrase - - - %d words - - To import your wallet, enter your seed phrase in the field below - Generate seed phrase - Import wallet - A seed phrase is a series of words that allows you to recover your wallet. Unlike the keys generated by the card, seed phrases are unprotected and can be copied and stolen. Use this option at your own risk. - Use seed phrase - Invalid seed phrase. Please check the word order. - Invalid seed phrase. Please check your spelling. - Legacy - To check whether you’ve written down your seed phrase correctly, please enter the 2nd, 7th and 11th words - So, let’s check - To start the backup process add up to two backup cards. - You can add one more card or finalize the backup process - Prepare the backup card with number %s - Scan the primary card to start the backup process. - Prepare the primary card with number %s - Your wallet card is configured and ready for use. - Max number of cards added. Finalize the backup process. - Activating card - Backup card #%d - No backup cards - One backup card added - Prepare your card - Two backup cards added - To get started, simply top up the wallet with any amount - To get started, simply top up the wallet with more than %1$s %2$s - Buy crypto - Show the wallet\'s address - Activate a wallet - The twinning process is partly complete. You can\'t exit it now. - If the process of creating the wallet gets interrupted in any way, you\'ll have to start over - You can backup your keys up to two other blank Tangem Wallet cards. - Access code can be restored with one of backup cards. - All the backup cards can be used as full-functional with the identical keys. - You will be able to set an access code to protect your wallets. - Backup wallet - Access code restore - Identical cards - Access code - Group - By balance - Organize tokens - Ungroup - Select from the gallery - Settings - You have not given access to your camera - Camera access denied - %1$s (%2$s) on %3$s network - Send only %s to this address. Sending any other currency will result in its irreversible loss. - Participate - Failed to load the information about the referral program. Please try again later. - Failed to load the information about the referral program. Error code: %s. Please try again later. - Upcoming payments - Your friends bought - Less - More - No upcoming payments - - for %d wallet - for %d wallets - - Will get ^^%1$s^^ for each wallet bought by your friend on your %2$s network address %3$s ^^30 days after^^ that - You - Will get a - when buying a wallet on tangem.com - %s discount - Your friend - Personal code copied! - Your personal code - Buy Tangem Wallet with discount!\n%s - Refer your friends to Tangem - You\'ve accepted - By tapping this button you accept - of the referral program - - %d wallet - %d wallets - - Reset the Card - I understand that after performing this action, I will no longer have access to the current wallet - I realize that I can\'t use this card to recover my access code on the other cards of the current wallet - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. - Do you have a bank card from another country and a residence permit or registration outside the Russian Federation? - Russian bank cards are not currently accepted - Log into the app and check your balance without scanning the card - Access the app - Allow to use biometrics - Biometrics will be requested instead of the access code for interactions with your wallet - Access code - It looks like you have biometric authentication disabled, it is necessary to save wallets - Enable biometric authorization - Would you like to use biometrics? - Note that making a transaction with your funds will still require your card - Scan Card - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Get your card ready! - Already included in the entered address - The commission amount is %s times the recommended amount. Make sure that the custom settings are correct. - You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue? - Reason: %1$s\nCode: %2$s - The transaction is not completed - Amount - You can set your transaction fee by adjusting the value in the Satoshi per vByte field. - Max fee - This is the cost you are willing to pay for each unit of gas. The higher the gas price, the faster your transaction will be processed. (Priority fee included) - Priority fee - The fee that a user can pay to miners or validators to expedite the inclusion of their transaction in a block. - %1$s, %2$s - Address - Destination Tag - Enter address - Address is the same as wallet address - The fee that will be charged for your transaction. You can set your own value. - Invalid Tag. It won\'t be added to the transaction. - Invalid Memo. It won\'t be added to the transaction. - Tag - Memo - Include fee - Low - Normal - Priority - Check your network connection - Network fee info unreachable - From - Gas limit - This is the maximum amount of gas that will be spent to complete a transaction or contract. A gas limit prevents unexpected or unlimited charges when executing a transaction. - Gas price - This is the cost you are willing to pay for each unit of gas. The higher the gas price, the faster your transaction will be processed. - Max - Maximum amount - Fee up to - Invalid Memo - Network fee coverage - Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance - Total exceeds balance - The account will be wiped from the blockchain if a balance goes below the existential deposit. Please leave %s on your balance. - Existential deposit - The commission amount is %s times the recommended amount. Make sure that the custom settings are correct. - Custom fee is high - Due to the peculiarities of the %1$s network, the fee for transferring the entire balance is higher. To reduce the commission, you can leave %2$s. - The fee is higher - The included commission exceeds the transfer amount, leading to a negative value - Invalid amount - The minimum sending amount is %1$s. Please ensure that the remaining balance after sending will not be less than %2$s. - Target account is not created. Please change the amount to send. - The amount to send must be at least %s - Leave %s - Reduce by %s - Reduce to %s - Kindly be aware that your transaction may experience delays under specific fee settings - Transaction delays are possible - Due to %1$s limitations only %2$s UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount. - Transaction limitation - Optional - Please align your QR code with the square to scan it. Ensure you scan %s network address. - Recent - Recipient - Not a valid address - Ensure the receiving wallet address is on the %s network to avoid losing your tokens - Send to - A Memo/Destination Tag is a unique ID for differentiating transactions sent to the same recipient on the same network. Caution: Omitting a memo may lead to misplaced funds - My wallets - A way of measuring Bitcoin transaction fees. It indicates the number of the smallest Bitcoin unit (Satoshi) for each virtual byte in a transaction. The higher the number, the faster the transaction will be processed by miners. - Satoshi / vByte - Sending... - Tap any field to change it - Send %s - You are sending **%1$s** including a network fee of %2$s - You are sending **%1$s** and %2$s - Sending %s - Total - %1$s and %2$s will be sent - ≈ %1$s (inc. fee: %2$s) - %s will be sent - Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while - Invalid address - %1$s (%2$s) - Transaction sent - Forget wallet - This will remove the wallet from the application. The wallet itself can be added again. - Name - Store your crypto assets secure while keeping private keys contained in your card - Revolutionary Hardware Wallet - Up to 3 physical cards to one wallet - Ultra Secure Backup - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card - Thousands of Currencies - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. - The Wallet for Everyone - Meet Tangem - Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services - Web 3.0 Compatible - Exchange more tokens at better rates directly in your wallet. - New Swap Provider Available! - The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address. - The amount includes the service provider\'s fee. - Fees - 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. - Approve - You swap - Give Permission - Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome. - Insufficient funds - Approve - Current transaction - The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap. - Give Permission - Specify the approve limit for the selected token - Amount %s - To continue, grant %1$s smart contracts permission to use your %2$s - Unlimited - In progress - Swap - You receive - Choose token - not available - Balances hidden - Balances shown - Undo - This operation is currently unavailable. Please try again later. - Buying %s is not available at the moment. Please check our updates. - You do not have funds to sell. Top up your account to be able to sell funds from it. - You do not have funds to send. Top up your account to be able to send funds from it. - Swapping %s is not available at the moment. Please check our updates. - Selling funds will be available once the pending transaction(s) in network %s is complete - Sending funds will be available once the pending transaction(s) in network %s is complete - Selling %s is not available at the moment. Please check our updates. - Generate XPUB - Hide - You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. - Hide %s - Hide token - Staking allows you to earn %1$s and get rewards every %2$s days - Earn up to %s staking rewards yearly - %1$s token in %%image%% %2$s network - Token in %%image%% %1$s network - The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list - Unable to hide %s - Exchange this token for another at %1$s service fees from February %2$s-%3$s. - Swap with Changelly, %s fees - Swap now - contract: %s - You don\'t have any transactions yet - Failed to load transaction history.\nClick on reload button to update the information. - Multiple addresses - Transaction history is currently not supported for this blockchain. But don\'t worry, we\'re working on it! In the meantime you can check it in the explorer. - Operation - from: %s - to: %s - You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d - You\'ve scanned wrong twin card. Please try another one - This one that you are holding in your hands and the other one with number %s.\n\nBoth cards can be used to extract funds from this wallet. - One wallet. Two cards. - Scan the card #%s - Creating wallet - Scan the #%s twin card - Preparing card - Tangem Twin - This action is irreversible. You will not have access to the old wallet. - Tap the twin card with number %s and do not remove until the end of the operation - Use %s or scan a card to have an access to your wallet - Add new wallet - Are you sure you want to delete this wallet? - An error has occurred, please scan your card to log in - This wallet has already been saved, you can add another one - The wallet with name %s already exists - Wallet name - Rename Wallet - Unlock all - Unlock all with %s - Blockchain is unreachable. Try later - Scan the card - Requesting to sign a message.\n\n%s - Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s - Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s - Transaction details:\nFrom: %1$s\nTo: %2$s\nAmount: %3$s - Clipboard contain WalletConnect code. Use copied value or scan QR-code - Request to create transaction for %1$s\n%2$s\n\nAmount: %3$s\nFee: %4$s\nTotal: %5$s\nBalance: %6$s - Can\'t send transaction. Not enough funds. - Failed to establish WalletConnect session. Please, try again later. - Not all tokens were added to your list. Please add them first and try again. Missing tokens:\n - Failed to sign message.\nPlease, try again - Failed to establish WalletConnect session: timeout error. Please, try again later. - Session request contains unsupported blockchains for WalletConnect connection. Unsupported blockchains:\n - Connection with this Dapp cannot be established due to its technical implementation. - We\'ve encountered unknown error. Error message: %s. If the problem persists — feel free to contact our support - Wrong card selected in Tangem App - Failed to create transaction from Dapp data. Code: %s - We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support - No opened WalletConnect sessions - Ooops. No Sessions. - Failed to pairing WalletConnect session: %1$s - Paste from clipboard - Message for %1$s:\n%2$s - Request to start a session for\n%1$s\n\nNETWORK: %2$s\n\nURL: %3$s - The operation couldn\'t be completed.\n\nYou have already established a WalletConnect session with this parameters. - Scan new code - This card can\'t be used to establish WalletConnect session - This network is not supported. Please select another network. - Select network - WalletConnect Sessions - Connect to dApps - WalletConnect - %s Market Price - last 24h - %s network - Address was copied to clipboard - No internet connection - Wallet settings - Tangem - Use %s or scan a card to unlock access to your wallet - Please withdraw all funds from this wallet, reset it to factory settings, and create a new one. Access to the current wallet will be lost. - Activation error - According to BNB network developers, support for the BEP-2 standard will end in June 2024. To avoid losing assets with this standard, please convert them to the BEP-20 standard. Use our swap service or third-party services to transfer funds to the BNB Smart Chain network. - BNB Beacon Chain will shut down - Could be better - Like it - Ok, Got it! - Really cool! - Refresh - You are currently in the Demo mode - Demo mode active - The card you scanned is a developer card. Do not use it to create your wallet. - Not for users! - %1$s network requires an Existential Deposit. If your account drops below %2$s, it will be deactivated, and any remaining funds will be destroyed. - Network requires Existential Deposit - Swap will be available after the %s transaction is complete - You have active transaction - Swap approval is underway and will be completed shortly - Approval in progress - The minimum swapping amount is %1$s. Please ensure that the remaining balance after the swap will not be less than %2$s. - You do not have any %s exchangeable coins in your list - No available tokens to swap - To make a transaction you need to deposit some %1$s %2$s - Unable to cover %s fee - The amount to receive must be at least %s - Service temporarily unavailable - The amount of tokens to be swapped must not exceed %s - The amount to swap must be at least %s - Please change the amount to swap - This card might be a production sample or counterfeit - Authenticity check failed - Associate - This token must be associated with your Hedera account before you can receive it. Association fee ~%1$s %2$s - This token must be associated with your Hedera account before you can receive it - Associate your token - Not enough %s. Top up your Hedera account to associate this token - Only %s signatures are left on this card. You must withdraw all of your funds. - Low signature count - Tokens on different networks can have different addresses. Double-check that your address matches the network when you transfer funds. - - Use your card to get an address for %d network - Use your card to get an addresses for %d networks - - Some addresses are missing - The network is currently unreachable. Please try again later. - Network is unreachable - Top up your wallet - Your wallet hasn\'t been backed up. Carry out this procedure to protect your assets now. - Missing backup - This card has been previously used for transactions. If received from an untrusted source, consider withdrawing all funds. If it\'s your card, no action is required. - Card has already signed transactions - Your review keeps us motivated to make Tangem Wallet even better - Enjoying Tangem? - You must associate your token before receiving tokens - Network rent fee required - %1$s is an asset in the %2$s network. To make a %3$s transaction, you must deposit some %4$s (%5$s) to cover the network fee. - Insufficient %1$s to cover network fee - The Solana network is congested. If your transaction is not processed within 2 minutes, please repeat the transaction. - Solana Network Alert - 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. - Some networks currently are unreachable. Please try again later. - Some networks are unreachable - This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes. - For testing purposes only - Discard - You have an interrupted backup. Do you want to resume? - Yes, resume - Discard - If you will discard the backup now, then you will have to reset the cards to factory settings to start over again - Resume backup - This is an irreversible action - Log in with %s - Scan card - Use %s or scan a card to access the app - Welcome back! + Add custom token + Manage tokens + Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds. + Request support + This feature is disabled in Demo mode + Reason: %s + Can\'t send a transaction + The selected does not support the %1$s network + To activate the %1$s blockchain\'s cryptographic encryption, you\'ll need to reset the wallet to factory settings. Please withdraw your funds before doing so to ensure that you don\'t lose them, and then complete the reset process. Access to the current wallet will not be possible after the reset. + Tokens in %1$s network are not supported by this card due to firmware limitation. + Are you having difficulty scanning your card? + This card is not designed to work with this app + Default Fee + Enable Default Fee to set transaction fees automatically and skip the Fee page when sending funds. You can always go back to this page if necessary. + Go to settings to enable biometric authentication in the Tangem App + Enable biometric authentication + This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. + Removing the saved card deletes all the saved wallets and their access codes from the app. + Save Access Code + Biometric authentication will be requested instead of the access code for interactions with your card. + Keep the wallet in the app + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Dark + Light + System default + Theme + App settings + To hide or show your balances, simply flip your device screen down, or switch it off in Settings + Don\'t show again + Got it + Balances are hidden + Please scan the card + Please try again in 30 seconds or scan the card + Too many attempts + You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. + Start backup process + + %d card + %d cards + + Disable this option if you don\'t want this card to be used to reset access codes on other cards in this wallet. Please note that this will also prevent you from resetting the access code on this card. + Allows you to use this card to reset access code on other cards in this wallet + Access code recovery + Reset + Are you sure you want to do this? + Change Access Code + Access code will be changed on this card only + All cards in the selected wallet have been reset to factory settings. You can now create a new wallet. + Reset complete + Do you want to reset the next card in this wallet? + Card reset + We recommend completing the reset process for all cards in this wallet + You haven\'t reset all your cards + Reset to Factory Settings + Security Mode + Card settings + In addition to network fee, the Cardano network charges %1$s ADA when transacting with the %2$s token + Cardano transaction requirements + To make a %1$s transaction, you must deposit some ADA to cover the network fee and minimum ADA value (5 ADA recommended) + Insufficient ADA for token transfer + You must maintain some ADA because you have some tokens on the Cardano blockchain + Not enough ADA + Accept + Access denied + Apply + Approval + Attention + Balance: %s + Balance + biometric authentication + biometrics + Buy + Go to %1$s + You have not given access to your camera, please adjust your privacy settings + Cancel + Close + Continue + Copy + Copy address + Create + Delete + Disabled + Done + Enable + Enabled + Error + Explore + Explore transaction history + Explorer + Fee + Network fees are charges users pay to process and confirm transactions. The fee amount can be affected by network congestion, transaction size, and execution priority. %s + Custom + Fast + Market + Slow + Speed and fee + Get addresses + Go to provider + Go to token + Import + Later + Locked + Main network + Network fee + Amount sent will be reduced by %1$s (%2$s) to cover the selected fee level + Next + No + No address + OK + Primary Card + Passphrase + Paste + Read more + Receive + Reject + Reload + Rename + Save changes + Search + Search tokens + Seed phrase + Select action + Sell + Send + The server is not available, please try again later + Share + Sign + Sign and send + Start + Submit + Success + Support + Swap + terms and conditions + Transaction failed + Transactions + Transfer + I understand + There was an error. Please try again. + Unreachable + Yes + Contract address copied! + Available networks + Add token + Contract address + Contract address is invalid + Please select the network + Decimal must be a valid integer, up to %li + Custom derivation + E. g. m/00\'/0000\'/0\'/0/0 + Enter custom derivation + Decimals + Derivation Path + Default + BIP44 coin type + The derivation path you\'ve entered is not valid + E.g. USD Coin + Name + Not selected + Network + Token network + You can manually add a token that is not natively supported by Tangem + E.g. USDC + Symbol + Token symbol + This token/network has already been added to your list + Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. + Be aware of adding scam tokens, they can cost nothing + Note that tokens can be created by anyone + Chat + Access code + You will have to submit the correct access code before scanning the card + Long Tap + This mechanism protects against proximity attacks on a card. It will enforce a delay between reception and execution of a command. + Passcode + Before executing any command entailing a change of the card state, you will have to enter the passcode. + Referral program + Flip your device screen down to quickly hide and show balances + %s hashes + Card ID + Contact support + Link More Cards + App Currency + Flip-to-Hide Balances + Issuer + Signed + Details + Check your internet connection or switch to a different network + Terms of service + You have used a card from another wallet. Tap the card associated with this wallet + My tokens + You haven\'t added any tokens yet. Add tokens via Market to swap + Cannot be swapped for %s + Provided by + Status + Tangem offers token swaps via 3rd-party providers according to each provider\'s terms + Choose provider + An error occurred. Code: %s + Oops! Swapping the selected pair through the chosen provider is temporarily unavailable. Please try again later. (Code: %s) + Selected provider is unavailable at the moment. Please try again later. (Code: %s) + Swaps are unavailable at the moment. Please try again later. (Code: %s) + Estimated amount + Exchange by %s + Visit provider’s website to refund your money + Operation failed by provider + The transaction amount was refunded in %1$s to your wallet due to OKX or bridge rules. %2$s + The amount was refunded in %1$s (%2$s network) + Visit provider’s website for verification + KYC verification required by provider + Canceled + Confirmed + Confirming + Confirming... + Exchanged + Exchanging + Exchanging... + Failed + Deposit received + Awaiting deposit + Awaiting deposit... + Refunded + Sending to you + Sending to you... + Sent + Provider-sourced data. Estimated amount subject to change due to market conditions. + Exchange status + Verification required + Awaiting transaction hash + List of all tokens added to your wallet + Fetching best rates... + Floating rate + By using swap functionality, you agree with provider’s %s + By using swap functionality, you agree with provider’s %1$s and %2$s + More providers are coming soon.\nStay tuned! + Privacy Policy + Provider + Best rate + Available up to %s + Available from %s + Unavailable for this pair + Permission Required + Recommended + Terms of Use + No tokens found. Please try another request + ID: %s + Transaction ID copied + The following information is optional. You can erase it if you don\'t want to share it. + Tell us what functions you are missing, and we will try to help you. + Please tell us what card do you have + Hi support team, + Please tell us more about your issue. Every small detail can help. + My suggestions + Can\'t scan a card + Feedback + Tangem feedback + Can\'t send a transaction + Order card + Scan card + To change the access code tap the card as shown above and do not remove until the end of the operation + To change the passcode tap the card as shown above and do not remove until the end of the operation + To create the wallet tap the card as shown above and do not remove until the end of the operation + Tap the card #%s of the wallet + Tap to scan + Tap to sign + Tap the card + You have updated biometrics, scan your card to enter + Your balance should be higher than the fee value to make a transfer + Not enough balance + You don\'t have enough Mana for this transaction. Please wait until the Mana is refilled. Your Mana balance is %1$s/%2$s + Not enough Mana + You can transfer only %s due to the Mana limit imposed by the Koinos network + Mana limit + The Koinos network requires Mana for network fees. Your have %1$s/%2$s Mana + Mana level + To begin tracking your crypto assets and transactions, add tokens + Manage tokens + To access all the networks you need to scan the card + Scan your card + Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s + Swap with Changelly, %s fees + Tokens + Book now + Save **$50** while booking via our partner Travala: **%1s - %2s** + Book your holidays with Tangem and pay in crypto + Add + Edit + Coin market cap + Blockchain the cryptocurrency was initially created + Native network + Using non-native networks for tokens enables cross-blockchain interoperability, allowing assets to be utilized in diverse decentralized applications and smart contracts across platforms. However, this often involves a custodian or smart contract to hold the original asset securely, introducing centralization and counterparty risk. + Not original or primary blockchain the token is hosted + Non-native networks + Choose networks + Wallet + Couldn’t find this token, you can add it manually + + %1$d of %2$d wallet + %1$d of %2$d wallets + + e.g. BTC I trust, hodl I must + The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it. + Upvote + Choose wallet + The wallet doesn\'t support more than one network + You have to set up a single access code to protect all your cards + Protect + You can set up an individual access code on each card later + Personalize + The access code can be restored with a linked card. Don’t keep all cards at one place. + Restore + Choose any word, phrase, or number you want as your access code + Create Access Code + Enter your access code one more time to avoid a mistake + Re-enter your Access Code + Access code must be at least 4 characters long + Entered access code didn\'t match the initial access code + Please repeat the operation. The card will be reset to factory settings. + Activation error + Add tokens + You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process? + The backup process is partly complete. You can\'t exit it now. + The passphrase is an advanced security feature that crypto wallets use. It adds an extra word or phrase of your own choosing to your already existing recovery phrase to unlock a brand-new set of addresses. + Add a backup card + Scan the card #%d + Backup now + Scan the primary card + Continue to my wallet + Finalize the backup + Receive crypto + Scan primary card + Skip for later + How does it work? + Let\'s generate all the keys on your card and create a secure wallet + Create wallet + Create a wallet + Other options + Your keys will be securely generated inside the card. There is no seed phrase, which means nobody can export or steal it. + Generate keys privately + Your card is activated and ready to be used + Success! + In this case, you will need to start from the beginning. + Do you want to exit the activation process? + Getting started + Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup. + Creating a backup + Read more about seed phrase + + + Write these %d words down in the order given below and store them in a safe and secret place. + + Your seed phrase + + + %d words + + To import your wallet, enter your seed phrase in the field below + Generate seed phrase + Import wallet + A seed phrase is a series of words that allows you to recover your wallet. Unlike the keys generated by the card, seed phrases are unprotected and can be copied and stolen. Use this option at your own risk. + Use seed phrase + Invalid seed phrase. Please check the word order. + Invalid seed phrase. Please check your spelling. + Legacy + To check whether you’ve written down your seed phrase correctly, please enter the 2nd, 7th and 11th words + So, let’s check + To start the backup process add up to two backup cards. + You can add one more card or finalize the backup process + Prepare the backup card with number %s + Scan the primary card to start the backup process. + Prepare the primary card with number %s + Your wallet card is configured and ready for use. + Max number of cards added. Finalize the backup process. + Activating card + Backup card #%d + No backup cards + One backup card added + Prepare your card + Two backup cards added + To get started, simply top up the wallet with any amount + To get started, simply top up the wallet with more than %1$s %2$s + Buy crypto + Show the wallet\'s address + Activate a wallet + The twinning process is partly complete. You can\'t exit it now. + If the process of creating the wallet gets interrupted in any way, you\'ll have to start over + You can backup your keys up to two other blank Tangem Wallet cards. + Access code can be restored with one of backup cards. + All the backup cards can be used as full-functional with the identical keys. + You will be able to set an access code to protect your wallets. + Backup wallet + Access code restore + Identical cards + Access code + Group + By balance + Organize tokens + Ungroup + Select from the gallery + Settings + You have not given access to your camera + Camera access denied + %1$s (%2$s) on %3$s network + Send only %s to this address. Sending any other currency will result in its irreversible loss. + Participate + Failed to load the information about the referral program. Please try again later. + Failed to load the information about the referral program. Error code: %s. Please try again later. + Upcoming payments + Your friends bought + Less + More + No upcoming payments + + for %d wallet + for %d wallets + + Will get ^^%1$s^^ for each wallet bought by your friend on your %2$s network address %3$s ^^30 days after^^ that + You + Will get a + when buying a wallet on tangem.com + %s discount + Your friend + Personal code copied! + Your personal code + Buy Tangem Wallet with discount!\n%s + Refer your friends to Tangem + You\'ve accepted + By tapping this button you accept + of the referral program + + %d wallet + %d wallets + + Reset the Card + I understand that after performing this action, I will no longer have access to the current wallet + I realize that I can\'t use this card to recover my access code on the other cards of the current wallet + Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Do you have a bank card from another country and a residence permit or registration outside the Russian Federation? + Russian bank cards are not currently accepted + Log into the app and check your balance without scanning the card + Access the app + Allow to use biometrics + Biometrics will be requested instead of the access code for interactions with your wallet + Access code + It looks like you have biometric authentication disabled, it is necessary to save wallets + Enable biometric authorization + Would you like to use biometrics? + Note that making a transaction with your funds will still require your card + Scan Card + Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. + Get your card ready! + Already included in the entered address + The commission amount is %s times the recommended amount. Make sure that the custom settings are correct. + You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue? + Reason: %1$s\nCode: %2$s + The transaction is not completed + Amount + You can set your transaction fee by adjusting the value in the Satoshi per vByte field. + Max fee + This is the cost you are willing to pay for each unit of gas. The higher the gas price, the faster your transaction will be processed. (Priority fee included) + Priority fee + The fee that a user can pay to miners or validators to expedite the inclusion of their transaction in a block. + %1$s, %2$s + Address + Destination Tag + Enter address + Address is the same as wallet address + The fee that will be charged for your transaction. You can set your own value. + Invalid Tag. It won\'t be added to the transaction. + Invalid Memo. It won\'t be added to the transaction. + Tag + Memo + Include fee + Low + Normal + Priority + Check your network connection + Network fee info unreachable + From + Gas limit + This is the maximum amount of gas that will be spent to complete a transaction or contract. A gas limit prevents unexpected or unlimited charges when executing a transaction. + Gas price + This is the cost you are willing to pay for each unit of gas. The higher the gas price, the faster your transaction will be processed. + Max + Maximum amount + Fee up to + Invalid Memo + Network fee coverage + Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance + Total exceeds balance + The account will be wiped from the blockchain if a balance goes below the existential deposit. Please leave %s on your balance. + Existential deposit + The commission amount is %s times the recommended amount. Make sure that the custom settings are correct. + Custom fee is high + Due to the peculiarities of the %1$s network, the fee for transferring the entire balance is higher. To reduce the commission, you can leave %2$s. + The fee is higher + The included commission exceeds the transfer amount, leading to a negative value + Invalid amount + The minimum sending amount is %1$s. Please ensure that the remaining balance after sending will not be less than %2$s. + Target account is not created. Please change the amount to send. + The amount to send must be at least %s + Leave %s + Reduce by %s + Reduce to %s + Kindly be aware that your transaction may experience delays under specific fee settings + Transaction delays are possible + Due to %1$s limitations only %2$s UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount. + Transaction limitation + Optional + Please align your QR code with the square to scan it. Ensure you scan %s network address. + Recent + Recipient + Not a valid address + Ensure the receiving wallet address is on the %s network to avoid losing your tokens + Send to + A Memo/Destination Tag is a unique ID for differentiating transactions sent to the same recipient on the same network. Caution: Omitting a memo may lead to misplaced funds + My wallets + A way of measuring Bitcoin transaction fees. It indicates the number of the smallest Bitcoin unit (Satoshi) for each virtual byte in a transaction. The higher the number, the faster the transaction will be processed by miners. + Satoshi / vByte + Sending... + Tap any field to change it + Send %s + You are sending **%1$s** including a network fee of %2$s + You are sending **%1$s** and %2$s + Sending %s + Total + %1$s and %2$s will be sent + ≈ %1$s (inc. fee: %2$s) + %s will be sent + Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while + Invalid address + %1$s (%2$s) + Transaction sent + Forget wallet + This will remove the wallet from the application. The wallet itself can be added again. + Name + Store your crypto assets secure while keeping private keys contained in your card + Revolutionary Hardware Wallet + Up to 3 physical cards to one wallet + Ultra Secure Backup + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + Thousands of Currencies + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + The Wallet for Everyone + Meet Tangem + Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services + Web 3.0 Compatible + Exchange more tokens at better rates directly in your wallet. + New Swap Provider Available! + The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address. + The amount includes the service provider\'s fee. + Fees + 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. + Approve + You swap + Give Permission + Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome. + Insufficient funds + Approve + Current transaction + The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap. + Give Permission + Specify the approve limit for the selected token + Amount %s + To continue, grant %1$s smart contracts permission to use your %2$s + Unlimited + In progress + Swap + You receive + Choose token + not available + Balances hidden + Balances shown + Undo + This operation is currently unavailable. Please try again later. + Buying %s is not available at the moment. Please check our updates. + You do not have funds to sell. Top up your account to be able to sell funds from it. + You do not have funds to send. Top up your account to be able to send funds from it. + Swapping %s is not available at the moment. Please check our updates. + Selling funds will be available once the pending transaction(s) in network %s is complete + Sending funds will be available once the pending transaction(s) in network %s is complete + Selling %s is not available at the moment. Please check our updates. + Generate XPUB + Hide + You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. + Hide %s + Hide token + Staking allows you to earn %1$s and get rewards every %2$s days + Earn up to %s staking rewards yearly + %1$s token in %%image%% %2$s network + Token in %%image%% %1$s network + The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list + Unable to hide %s + Exchange this token for another at %1$s service fees from February %2$s-%3$s. + Swap with Changelly, %s fees + Swap now + contract: %s + You don\'t have any transactions yet + Failed to load transaction history.\nClick on reload button to update the information. + Multiple addresses + Transaction history is currently not supported for this blockchain. But don\'t worry, we\'re working on it! In the meantime you can check it in the explorer. + Operation + from: %s + to: %s + You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d + You\'ve scanned wrong twin card. Please try another one + This one that you are holding in your hands and the other one with number %s.\n\nBoth cards can be used to extract funds from this wallet. + One wallet. Two cards. + Scan the card #%s + Creating wallet + Scan the #%s twin card + Preparing card + Tangem Twin + This action is irreversible. You will not have access to the old wallet. + Tap the twin card with number %s and do not remove until the end of the operation + Use %s or scan a card to have an access to your wallet + Add new wallet + Are you sure you want to delete this wallet? + An error has occurred, please scan your card to log in + This wallet has already been saved, you can add another one + The wallet with name %s already exists + Wallet name + Rename Wallet + Unlock all + Unlock all with %s + Blockchain is unreachable. Try later + Scan the card + Requesting to sign a message.\n\n%s + Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s + Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s + Transaction details:\nFrom: %1$s\nTo: %2$s\nAmount: %3$s + Clipboard contain WalletConnect code. Use copied value or scan QR-code + Request to create transaction for %1$s\n%2$s\n\nAmount: %3$s\nFee: %4$s\nTotal: %5$s\nBalance: %6$s + Can\'t send transaction. Not enough funds. + Failed to establish WalletConnect session. Please, try again later. + Not all tokens were added to your list. Please add them first and try again. Missing tokens:\n + Failed to sign message.\nPlease, try again + Failed to establish WalletConnect session: timeout error. Please, try again later. + Session request contains unsupported blockchains for WalletConnect connection. Unsupported blockchains:\n + Connection with this Dapp cannot be established due to its technical implementation. + We\'ve encountered unknown error. Error message: %s. If the problem persists — feel free to contact our support + Wrong card selected in Tangem App + Failed to create transaction from Dapp data. Code: %s + We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support + No opened WalletConnect sessions + Ooops. No Sessions. + Failed to pairing WalletConnect session: %1$s + Paste from clipboard + Message for %1$s:\n%2$s + Request to start a session for\n%1$s\n\nNETWORK: %2$s\n\nURL: %3$s + The operation couldn\'t be completed.\n\nYou have already established a WalletConnect session with this parameters. + Scan new code + This card can\'t be used to establish WalletConnect session + This network is not supported. Please select another network. + Select network + WalletConnect Sessions + Connect to dApps + WalletConnect + %s Market Price + last 24h + %s network + Address was copied to clipboard + No internet connection + Wallet settings + Tangem + Use %s or scan a card to unlock access to your wallet + Please withdraw all funds from this wallet, reset it to factory settings, and create a new one. Access to the current wallet will be lost. + Activation error + According to BNB network developers, support for the BEP-2 standard will end in June 2024. To avoid losing assets with this standard, please convert them to the BEP-20 standard. Use our swap service or third-party services to transfer funds to the BNB Smart Chain network. + BNB Beacon Chain will shut down + Could be better + Like it + Ok, Got it! + Really cool! + Refresh + You are currently in the Demo mode + Demo mode active + The card you scanned is a developer card. Do not use it to create your wallet. + Not for users! + %1$s network requires an Existential Deposit. If your account drops below %2$s, it will be deactivated, and any remaining funds will be destroyed. + Network requires Existential Deposit + Swap will be available after the %s transaction is complete + You have active transaction + Swap approval is underway and will be completed shortly + Approval in progress + The minimum swapping amount is %1$s. Please ensure that the remaining balance after the swap will not be less than %2$s. + You do not have any %s exchangeable coins in your list + No available tokens to swap + To make a transaction you need to deposit some %1$s %2$s + Unable to cover %s fee + The amount to receive must be at least %s + Service temporarily unavailable + The amount of tokens to be swapped must not exceed %s + The amount to swap must be at least %s + Please change the amount to swap + This card might be a production sample or counterfeit + Authenticity check failed + Associate + This token must be associated with your Hedera account before you can receive it. Association fee ~%1$s %2$s + This token must be associated with your Hedera account before you can receive it + Associate your token + Not enough %s. Top up your Hedera account to associate this token + Only %s signatures are left on this card. You must withdraw all of your funds. + Low signature count + Tokens on different networks can have different addresses. Double-check that your address matches the network when you transfer funds. + + Use your card to get an address for %d network + Use your card to get an addresses for %d networks + + Some addresses are missing + The network is currently unreachable. Please try again later. + Network is unreachable + Top up your wallet + Your wallet hasn\'t been backed up. Carry out this procedure to protect your assets now. + Missing backup + This card has been previously used for transactions. If received from an untrusted source, consider withdrawing all funds. If it\'s your card, no action is required. + Card has already signed transactions + Your review keeps us motivated to make Tangem Wallet even better + Enjoying Tangem? + You must associate your token before receiving tokens + Network rent fee required + %1$s is an asset in the %2$s network. To make a %3$s transaction, you must deposit some %4$s (%5$s) to cover the network fee. + Insufficient %1$s to cover network fee + The Solana network is congested. If your transaction is not processed within 2 minutes, please repeat the transaction. + Solana Network Alert + 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. + Some networks currently are unreachable. Please try again later. + Some networks are unreachable + This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes. + For testing purposes only + Discard + You have an interrupted backup. Do you want to resume? + Yes, resume + Discard + If you will discard the backup now, then you will have to reset the cards to factory settings to start over again + Resume backup + This is an irreversible action + Log in with %s + Scan card + Use %s or scan a card to access the app + Welcome back! diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotification.kt new file mode 100644 index 0000000000..7c646add3d --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotification.kt @@ -0,0 +1,125 @@ +package com.tangem.core.ui.components.notifications + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.text.ClickableText +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.colorspace.ColorSpaces +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerW +import com.tangem.core.ui.components.currency.tokenicon.TokenIcon +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +/** + * Currency notification component from Design system. + * + * @param config component config + * @param modifier modifier + * @param containerColor container color + * + * @see Figma component + */ +@Composable +fun CurrencyNotification( + config: CurrencyNotificationConfig, + modifier: Modifier = Modifier, + containerColor: Color? = null, +) { + NotificationBaseContainer( + buttonsState = config.buttonsState, + onClick = null, + onCloseClick = null, + modifier = modifier, + containerColor = containerColor, + ) { + MainContent( + tokenIconState = config.tokenIconState, + title = config.title, + subtitle = config.subtitle, + ) + } +} + +@Composable +private fun MainContent( + tokenIconState: TokenIconState, + title: TextReference, + subtitle: CurrencyNotificationConfig.AnnotatedSubtitle, +) { + Row { + TokenIcon( + state = tokenIconState, + modifier = Modifier.align(alignment = Alignment.CenterVertically), + ) + + SpacerW(width = TangemTheme.dimens.spacing6) + + TextsBlock(title = title, subtitle = subtitle) + } +} + +@Composable +private fun TextsBlock(title: TextReference, subtitle: CurrencyNotificationConfig.AnnotatedSubtitle) { + Column(verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2)) { + Text( + text = title.resolveReference(), + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.button, + ) + + val subtitleValue = subtitle.valueProvider() + ClickableText( + text = subtitleValue, + onClick = { subtitle.onClick(subtitleValue, it) }, + style = TangemTheme.typography.caption2, + ) + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview_Notification() { + TangemThemePreview { + CurrencyNotification( + config = CurrencyNotificationConfig( + title = resourceReference( + R.string.express_exchange_notification_refund_title, + wrappedList("USDT", "Polygon"), + ), + subtitle = CurrencyNotificationConfig.AnnotatedSubtitle( + valueProvider = { + buildAnnotatedString { + append("Your transaction amount was refunded in USDT to your wallet due to OKX") + } + }, + onClick = { _, _ -> }, + ), + tokenIconState = TokenIconState.TokenIcon( + url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/usd-coin.png", + networkBadgeIconResId = R.drawable.ic_polygon_22, + isGrayscale = false, + showCustomBadge = false, + fallbackTint = Color(1.0f, 1.0f, 1.0f, 1.0f, ColorSpaces.Srgb), + fallbackBackground = Color(0.23529412f, 0.28627452f, 0.6117647f, 1.0f, ColorSpaces.Srgb), + ), + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = stringReference("Go to token"), + onClick = {}, + ), + ), + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotificationConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotificationConfig.kt new file mode 100644 index 0000000000..0fa3aadd26 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/CurrencyNotificationConfig.kt @@ -0,0 +1,35 @@ +package com.tangem.core.ui.components.notifications + +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.AnnotatedString +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.extensions.TextReference + +/** + * Currency notification component state + * + * @property title title + * @property subtitle subtitle + * @property buttonsState buttons state + * @property tokenIconState token icon state + * +[REDACTED_AUTHOR] + */ +data class CurrencyNotificationConfig( + val title: TextReference, + val subtitle: AnnotatedSubtitle, + val tokenIconState: TokenIconState, + val buttonsState: NotificationConfig.ButtonsState, +) { + + /** + * Subtitle as [AnnotatedString] + * + * @property valueProvider composable function that provides [AnnotatedString] + * @property onClick lambda be invoked when text in specified position is clicked + */ + data class AnnotatedSubtitle( + val valueProvider: @Composable () -> AnnotatedString, + val onClick: (value: AnnotatedString, position: Int) -> Unit, + ) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index c5b3d10a9b..f25c4014cf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -30,17 +30,19 @@ import com.tangem.core.ui.components.* import com.tangem.core.ui.components.buttons.common.TangemButtonSize import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState /** * Notification component from Design system. * Use this for Notification with title, subtitle, clickable or not. * - * @param config component config - * @param modifier modifier - * @param iconTint icon tint + * @param config component config + * @param modifier modifier + * @param containerColor container color + * @param iconTint icon tint + * @param isEnabled flag that defines if component is clickable * * @see Figma component @@ -53,44 +55,33 @@ fun Notification( iconTint: Color? = null, isEnabled: Boolean = true, ) { - BaseContainer( + NotificationBaseContainer( buttonsState = config.buttonsState, onClick = config.onClick, + onCloseClick = config.onCloseClick, modifier = modifier, containerColor = containerColor, isEnabled = isEnabled, ) { - Column( - modifier = Modifier.padding(all = TangemTheme.dimens.spacing12), - verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12), - ) { - MainContent( - iconResId = config.iconResId, - iconTint = iconTint, - title = config.title, - subtitle = config.subtitle, - isClickableComponent = isEnabled && config.onClick != null, - ) - - Buttons(state = config.buttonsState, isEnabled = isEnabled) - } - - CloseableIconButton( - onClick = config.onCloseClick, - modifier = Modifier.align(alignment = Alignment.TopEnd), - isEnabled = isEnabled, + MainContent( + iconResId = config.iconResId, + iconTint = iconTint, + title = config.title, + subtitle = config.subtitle, + isClickableComponent = isEnabled && config.onClick != null, ) } } @Composable -private fun BaseContainer( +internal fun NotificationBaseContainer( buttonsState: NotificationConfig.ButtonsState?, onClick: (() -> Unit)?, + onCloseClick: (() -> Unit)?, modifier: Modifier = Modifier, isEnabled: Boolean = true, containerColor: Color? = null, - content: @Composable BoxScope.() -> Unit, + content: @Composable ColumnScope.() -> Unit, ) { val tempContainerColor by rememberUpdatedState( newValue = if (buttonsState != null || onClick != null) { @@ -109,7 +100,22 @@ private fun BaseContainer( shape = TangemTheme.shapes.roundedCornersXMedium, color = containerColor ?: tempContainerColor, ) { - Box(content = content) + Box { + Column( + modifier = Modifier.padding(all = TangemTheme.dimens.spacing12), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12), + ) { + content() + + Buttons(state = buttonsState, isEnabled = isEnabled) + } + + CloseableIconButton( + onClick = onCloseClick, + modifier = Modifier.align(alignment = Alignment.TopEnd), + isEnabled = isEnabled, + ) + } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt index 58b0829319..fd9625e5c3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/ExchangeStatusNotifications.kt @@ -1,38 +1,43 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.components import androidx.compose.runtime.Immutable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withStyle +import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter +import com.tangem.core.ui.components.notifications.CurrencyNotificationConfig import com.tangem.core.ui.components.notifications.NotificationConfig -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.features.tokendetails.impl.R @Immutable -internal sealed class ExchangeStatusNotifications(val config: NotificationConfig) { +internal sealed interface ExchangeStatusNotifications { - data class NeedVerification( - val onGoToProviderClick: () -> Unit, - ) : ExchangeStatusNotifications( + sealed class CommonNotification(val config: NotificationConfig) : ExchangeStatusNotifications + + data class NeedVerification(val onGoToProviderClick: () -> Unit) : CommonNotification( config = NotificationConfig( - title = TextReference.Res(R.string.express_exchange_notification_verification_title), - subtitle = TextReference.Res(R.string.express_exchange_notification_verification_text), + title = resourceReference(R.string.express_exchange_notification_verification_title), + subtitle = resourceReference(R.string.express_exchange_notification_verification_text), iconResId = R.drawable.ic_alert_triangle_20, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = TextReference.Res(R.string.common_go_to_provider), + text = resourceReference(R.string.common_go_to_provider), onClick = onGoToProviderClick, ), ), ) - data class Failed( - val onGoToProviderClick: () -> Unit, - ) : ExchangeStatusNotifications( + data class Failed(val onGoToProviderClick: () -> Unit) : CommonNotification( config = NotificationConfig( - title = TextReference.Res(R.string.express_exchange_notification_failed_title), - subtitle = TextReference.Res(R.string.express_exchange_notification_failed_text), + title = resourceReference(R.string.express_exchange_notification_failed_title), + subtitle = resourceReference(R.string.express_exchange_notification_failed_text), iconResId = R.drawable.ic_alert_circle_24, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = TextReference.Res(R.string.common_go_to_provider), + text = resourceReference(R.string.common_go_to_provider), onClick = onGoToProviderClick, ), ), @@ -40,16 +45,48 @@ internal sealed class ExchangeStatusNotifications(val config: NotificationConfig data class TokenRefunded( val cryptoCurrency: CryptoCurrency, + val onReadMoreClick: () -> Unit, val onGoToTokenClick: () -> Unit, - ) : ExchangeStatusNotifications( - config = NotificationConfig( - title = stringReference("TITLE FOR TOKEN REFUND"), - subtitle = stringReference("SUBTITLE FOR TOKEN REFUND"), - iconResId = R.drawable.ic_alert_triangle_20, + ) : ExchangeStatusNotifications { + + val config = CurrencyNotificationConfig( + title = resourceReference( + id = R.string.express_exchange_notification_refund_title, + formatArgs = wrappedList(cryptoCurrency.symbol, cryptoCurrency.network.name), + ), + subtitle = CurrencyNotificationConfig.AnnotatedSubtitle( + valueProvider = { + val linkText = stringResource(R.string.common_read_more) + val fullString = stringResource( + R.string.express_exchange_notification_refund_text, + cryptoCurrency.symbol, + linkText, + ) + + val linkTextPosition = fullString.length - linkText.length + + buildAnnotatedString { + withStyle(SpanStyle(TangemTheme.colors.text.tertiary)) { + append(fullString.substring(0, linkTextPosition)) + } + + withStyle(SpanStyle(TangemTheme.colors.text.accent)) { + append(fullString.substring(linkTextPosition, fullString.length)) + } + } + }, + onClick = { value, position -> + val readMoreStyle = requireNotNull(value.spanStyles.getOrNull(1)) + if (position in readMoreStyle.start..readMoreStyle.end) { + onReadMoreClick() + } + }, + ), + tokenIconState = CryptoCurrencyToIconStateConverter().convert(cryptoCurrency), buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = stringReference("Go to token"), + text = resourceReference(R.string.common_go_to_token), onClick = onGoToTokenClick, ), - ), - ) + ) + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index fba5085305..54d15b83ea 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -25,6 +25,7 @@ import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import java.math.BigDecimal +import java.util.Locale internal class TokenDetailsSwapTransactionsStateConverter( private val clickIntents: TokenDetailsClickIntents, @@ -163,9 +164,11 @@ internal class TokenDetailsSwapTransactionsStateConverter( if (refundToken == null) { null } else { - ExchangeStatusNotifications.TokenRefunded(refundToken) { - clickIntents.onGoToRefundedTokenClick(refundToken) - } + ExchangeStatusNotifications.TokenRefunded( + cryptoCurrency = refundToken, + onReadMoreClick = { clickIntents.onOpenUrlClick(url = getAboutCrossChainBridgesLink()) }, + onGoToTokenClick = { clickIntents.onGoToRefundedTokenClick(refundToken) }, + ) } } else -> null @@ -316,4 +319,12 @@ internal class TokenDetailsSwapTransactionsStateConverter( isDone = isSendingDone, ) } + + private fun getAboutCrossChainBridgesLink(): String { + return if (Locale.getDefault().country == "RU") { + "https://tangem.com/ru/blog/post/an-overview-of-cross-chain-bridges/" + } else { + "https://tangem.com/en/blog/post/an-overview-of-cross-chain-bridges/" + } + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt index a1a7c595a5..d3372936b1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt @@ -17,11 +17,12 @@ import com.tangem.core.ui.components.SpacerH24 import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.components.notifications.CurrencyNotification import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications @Composable internal fun ExchangeStatusBottomSheet(config: TangemBottomSheetConfig) { @@ -29,16 +30,13 @@ internal fun ExchangeStatusBottomSheet(config: TangemBottomSheetConfig) { config = config, containerColor = TangemTheme.colors.background.tertiary, ) { content: ExchangeStatusBottomSheetConfig -> - ExchangeStatusBottomSheetContent(content = content) + ExchangeStatusBottomSheetContent(config = content.value) } } @Composable -private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetConfig) { - val config = content.value - Column( - modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), - ) { +private fun ExchangeStatusBottomSheetContent(config: SwapTransactionsState) { + Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { SpacerH10() Text( text = stringResource(id = R.string.express_exchange_status_title), @@ -80,25 +78,39 @@ private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetC showLink = config.showProviderLink, onClick = { config.onGoToProviderClick(config.txUrl.orEmpty()) }, ) - AnimatedContent( - targetState = config.notification, - label = "Exchange Status Notification Change", - ) { - it?.let { - val tint = when (config.activeStatus) { - ExchangeStatus.Verifying -> TangemTheme.colors.icon.attention - ExchangeStatus.Failed -> TangemTheme.colors.icon.warning - else -> null - } - Notification( - config = it.config, - iconTint = tint, + if (config.notification != null) { + Notification(state = config.notification, activeStatus = config.activeStatus) + } + SpacerH24() + } +} + +@Composable +private fun Notification(state: ExchangeStatusNotifications, activeStatus: ExchangeStatus?) { + AnimatedContent( + targetState = state, + modifier = Modifier.padding(top = TangemTheme.dimens.spacing12), + label = "Exchange Status Notification Change", + ) { notification -> + when (notification) { + is ExchangeStatusNotifications.CommonNotification -> { + com.tangem.core.ui.components.notifications.Notification( + config = notification.config, + iconTint = when (activeStatus) { + ExchangeStatus.Verifying -> TangemTheme.colors.icon.attention + ExchangeStatus.Failed -> TangemTheme.colors.icon.warning + else -> null + }, + containerColor = TangemTheme.colors.background.action, + ) + } + is ExchangeStatusNotifications.TokenRefunded -> { + CurrencyNotification( + config = notification.config, containerColor = TangemTheme.colors.background.action, - modifier = Modifier.padding(top = TangemTheme.dimens.spacing12), ) } } - SpacerH24() } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 387d2f3482..9781e7cca9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -57,4 +57,6 @@ interface TokenDetailsClickIntents { fun onAssociateClick() fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) + + fun onOpenUrlClick(url: String) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 9fa0481605..37e0da4d40 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -726,6 +726,10 @@ internal class TokenDetailsViewModel @Inject constructor( router.openTokenDetails(userWalletId, cryptoCurrency) } + override fun onOpenUrlClick(url: String) { + router.openUrl(url) + } + override fun onSwapPromoDismiss() { viewModelScope.launch(dispatchers.main) { shouldShowSwapPromoTokenUseCase.neverToShow() From 297eb15571612408957c03718be826dff0a01514 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 Jul 2024 13:18:35 +0300 Subject: [PATCH 12/17] Updated on 2026-08-14 --- .../datasource/api/tangemTech/TangemTechApi.kt | 1 + .../tangem/data/promo/DefaultPromoRepository.kt | 16 +++++++--------- .../repository/DefaultCurrenciesRepository.kt | 16 +++------------- ...TokenDetailsSwapTransactionsStateConverter.kt | 3 ++- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index eb9e2a434c..9fe41bf3a5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -18,6 +18,7 @@ interface TangemTechApi { @Query("contractAddress") contractAddress: String? = null, @Query("exchangeable") exchangeable: Boolean? = null, @Query("networkIds") networkIds: String? = null, + @Query("networkId") networkId: String? = null, @Query("active") active: Boolean? = null, @Query("searchText") searchText: String? = null, @Query("offset") offset: Int? = null, diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt index e48f0e6933..94a184fb73 100644 --- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt +++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt @@ -33,19 +33,17 @@ internal class DefaultPromoRepository( } override suspend fun getOkxPromoBanner(): PromoBanner? { - // TODO disabled for 5.12, enable for 5.12.1 - return null - // return runCatching(dispatchers.io) { - // promoResponseConverter.convert( - // tangemApi.getPromotionInfo(OKX) - // .getOrThrow(), - // ) - // }.getOrNull() + return runCatching(dispatchers.io) { + promoResponseConverter.convert( + tangemApi.getPromotionInfo(OKX) + .getOrThrow(), + ) + }.getOrNull() } private companion object { private const val CHANGELLY_NAME = "changelly" private const val TRAVALA = "travala" - // private const val OKX = "okx" + private const val OKX = "okx" } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 214d9721e1..653b731d38 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -429,22 +429,12 @@ internal class DefaultCurrenciesRepository( val token = withContext(dispatchers.io) { val foundToken = tangemTechApi.getCoins( contractAddress = contractAddress, - networkIds = networkId, + networkId = networkId, ) .getOrThrow() .coins - .firstNotNullOfOrNull { coin -> - val networksWithTheSameAddress = coin.networks.filter { network -> - (network.contractAddress != null || network.decimalCount != null) && - network.contractAddress?.equals(contractAddress, ignoreCase = true) == true - } - - if (networksWithTheSameAddress.isNotEmpty()) { - coin.copy(networks = networksWithTheSameAddress) - } else { - null - } - } ?: error("Token not found") + .firstOrNull() + ?: error("Token not found") val network = foundToken.networks.firstOrNull { it.networkId == networkId } ?: error("Network not found") CryptoCurrencyFactory.Token( symbol = foundToken.symbol, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index 54d15b83ea..12a65e1e6b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -142,9 +142,9 @@ internal class TokenDetailsSwapTransactionsStateConverter( txUrl: String?, refundToken: CryptoCurrency?, ): ExchangeStatusNotifications? { - if (txUrl == null) return null return when (status) { ExchangeStatus.Failed -> { + if (txUrl == null) return null ExchangeStatusNotifications.Failed { analyticsEventsHandlerProvider().send( TokenExchangeAnalyticsEvent.GoToProviderFail(cryptoCurrency.symbol), @@ -153,6 +153,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( } } ExchangeStatus.Verifying -> { + if (txUrl == null) return null ExchangeStatusNotifications.NeedVerification { analyticsEventsHandlerProvider().send( TokenExchangeAnalyticsEvent.GoToProviderKYC(cryptoCurrency.symbol), From f8ac204948b6f14684df17e7e927135a5160fb9c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 Jul 2024 17:28:17 +0300 Subject: [PATCH 13/17] Updated on 2026-08-14 --- .../java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 8402e1f8c0..9e7fd0aa87 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1271,7 +1271,7 @@ internal class SwapInteractorImpl @Inject constructor( val feeToCheckFunds = feeByPriority + (otherNativeFee ?: BigDecimal.ZERO) val isBalanceIncludeFeeEnough = isBalanceEnough(fromToken, amount, feeToCheckFunds) val feeState = getFeeState( - fee = feeByPriority, + fee = feeToCheckFunds, spendAmount = amount, networkId = networkId, fromTokenStatus = fromToken, From 6ee898a262738678dd96ba25df362cbac06bf3f1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 Jul 2024 17:48:08 +0300 Subject: [PATCH 14/17] Updated on 2026-08-14 --- .../tangem/tap/features/home/redux/HomeMiddleware.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 92efb060a0..42f21e542d 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -1,5 +1,7 @@ package com.tangem.tap.features.home.redux +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.ktx.Firebase import com.tangem.common.doOnFailure import com.tangem.common.doOnResult import com.tangem.common.doOnSuccess @@ -63,8 +65,13 @@ private fun handleHomeAction(action: Action) { } is HomeAction.GoToShop -> { Analytics.send(Shop.ScreenOpened()) - store.dispatchOpenUrl(NEW_BUY_WALLET_URL) - + Firebase.analytics.appInstanceId + .addOnSuccessListener { + store.dispatchOpenUrl("$NEW_BUY_WALLET_URL&app_instance_id=$it") + } + .addOnFailureListener { + store.dispatchOpenUrl(NEW_BUY_WALLET_URL) + } // disabled for now in task [REDACTED_JIRA] // when (action.userCountryCode) { // RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL) From cef5863d91da9e7ff0d9fd308275fc6374b4ec92 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 Jul 2024 14:50:38 +0300 Subject: [PATCH 15/17] Updated on 2026-08-14 --- .../tokendetails/viewmodels/ExchangeStatusFactory.kt | 5 +++-- .../tokendetails/viewmodels/TokenDetailsViewModel.kt | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index 8299f06c97..fc2733a1ba 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -84,12 +84,13 @@ internal class ExchangeStatusFactory( } } - suspend fun removeTransactionOnBottomSheetClosed(): TokenDetailsState { + suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false): TokenDetailsState { val state = currentStateProvider() val bottomSheetConfig = state.bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return state val selectedTx = bottomSheetConfig.value - return if (selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus)) { + val shouldTerminate = selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus) || isForceTerminal + return if (shouldTerminate) { swapTransactionRepository.removeTransaction( userWalletId = userWalletId, fromCryptoCurrency = selectedTx.fromCryptoCurrency, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 37e0da4d40..557903c7ca 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -718,8 +718,8 @@ internal class TokenDetailsViewModel @Inject constructor( override fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) { if (internalUiState.value.bottomSheetConfig?.content is ExchangeStatusBottomSheetConfig) { - viewModelScope.launch(dispatchers.main) { - internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed() + viewModelScope.launch { + internalUiState.value = exchangeStatusFactory.removeTransactionOnBottomSheetClosed(true) } } internalUiState.value = stateFactory.getStateWithClosedBottomSheet() From 2044fd0da2585e99eccda47353d418415b071a5f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 Jul 2024 15:15:37 +0300 Subject: [PATCH 16/17] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e5f1b0cf1f..5b292b2371 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -87,7 +87,7 @@ markdown = "0.7.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.12-701" +tangemBlockchainSdk = "release-app_5.12-715" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.12-373" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From dcc3286a085df353772998ae2f707b68c0fc403d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Aug 2024 10:38:08 +0300 Subject: [PATCH 17/17] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index b5726a45bf..095b8f4ea0 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit b5726a45bf51b7763c5967afae4d3d687676240b +Subproject commit 095b8f4ea0fa02e7ccea93cf0f437346345297ef