From c9d5d65799a858ba5bdeac7c9de90db381eb2bdd Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Aug 2022 12:49:06 +0400 Subject: [PATCH 01/32] Updated on 2026-08-14 --- .../features/details/ui/resetcard/ResetCardScreen.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index 2c07b092ad..eda04bf95c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.ui.resetcard import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -86,11 +87,14 @@ fun ResetCardView( color = colorResource(id = R.color.text_secondary), ) - Spacer(modifier = modifier.size(44.dp)) + Spacer(modifier = modifier.size(28.dp)) Row( modifier = modifier .fillMaxWidth() - .padding(end = 20.dp), + .clickable( + onClick = { state.onAcceptWarningToggleClick(!state.accepted) }, + ) + .padding(top = 16.dp, bottom = 16.dp), ) { IconToggleButton( checked = state.accepted, @@ -117,10 +121,12 @@ fun ResetCardView( text = stringResource(id = R.string.reset_card_to_factory_warning_message), style = TangemTypography.body2, color = colorResource(id = R.color.text_secondary), + modifier = modifier + .padding(end = 20.dp), ) } - Spacer(modifier = modifier.size(32.dp)) + Spacer(modifier = modifier.size(16.dp)) Box( modifier = modifier .padding(start = 16.dp, end = 16.dp, bottom = 32.dp), From d8be65f5f78d348599f3da3106d3d3bdaac14dfa Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 17 Sep 2022 15:04:50 +0400 Subject: [PATCH 02/32] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 18 +- .../java/com/tangem/tap/common/FileReader.kt | 20 ++ .../common/redux/global/GlobalMidlleware.kt | 4 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 8 +- .../java/com/tangem/tap/domain/TapErrors.kt | 13 +- .../com/tangem/tap/domain/TapWalletManager.kt | 80 +++-- .../domain/extensions/WalletManagerFactory.kt | 12 +- .../domain/tasks/product/ScanProductTask.kt | 84 +++--- .../tap/domain/tokens/CurrenciesRepository.kt | 256 +--------------- .../tokens/LoadAvailableCoinsService.kt | 28 +- .../domain/tokens/OldUserTokensRepository.kt | 130 ++++++++ .../domain/tokens/UserTokensNetworkService.kt | 30 ++ .../tap/domain/tokens/UserTokensRepository.kt | 117 ++++++++ .../domain/tokens/UserTokensStorageService.kt | 48 +++ .../walletconnect/WcWalletManagerFactory.kt | 64 ---- .../details/redux/DetailsMiddleware.kt | 6 +- .../walletconnect/WalletConnectMiddleware.kt | 67 ++--- .../tap/features/home/redux/HomeMiddleware.kt | 47 +-- .../twins/redux/TwinCardsMiddleware.kt | 2 - .../redux/OnboardingWalletMiddleware.kt | 13 +- .../features/tokens/redux/TokensMiddleware.kt | 26 +- .../tap/features/wallet/models/Currency.kt | 86 +++++- .../tap/features/wallet/redux/WalletAction.kt | 19 +- .../tap/features/wallet/redux/WalletState.kt | 30 +- .../middlewares/MultiWalletMiddleware.kt | 281 +++++++----------- .../redux/middlewares/WalletMiddleware.kt | 11 +- .../redux/reducers/MultiWalletReducer.kt | 11 +- .../wallet/redux/reducers/WalletReducer.kt | 11 +- .../wallet/ui/wallet/MultiWalletView.kt | 15 +- app/src/main/res/drawable/ic_scan_card.xml | 21 ++ app/src/main/res/layout/fragment_wallet.xml | 46 ++- .../layout/layout_wallet_rescan_warning.xml | 70 +++++ app/src/main/res/values-de/strings_final.xml | 2 + app/src/main/res/values-fr/strings_final.xml | 2 + app/src/main/res/values-it/strings_final.xml | 2 + app/src/main/res/values-ru/strings_final.xml | 2 + app/src/main/res/values/strings_final.xml | 2 + .../domain/common/extensions/ByteArray.kt | 10 + .../network/api/tangemTech/Responses.kt | 26 +- .../network/api/tangemTech/TangemTechApi.kt | 9 + .../api/tangemTech/TangemTechService.kt | 21 +- 41 files changed, 983 insertions(+), 767 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/FileReader.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tokens/OldUserTokensRepository.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tokens/UserTokensNetworkService.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt create mode 100644 app/src/main/res/drawable/ic_scan_card.xml create mode 100644 app/src/main/res/layout/layout_wallet_rescan_warning.xml create mode 100644 domain/src/main/java/com/tangem/domain/common/extensions/ByteArray.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 4674adf0e6..18cfcf9ff3 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -12,6 +12,7 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.domain.DomainLayer import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.AndroidAssetReader +import com.tangem.tap.common.AssetReader import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.FeedbackManager @@ -25,7 +26,7 @@ import com.tangem.tap.domain.configurable.config.Config import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.config.FeaturesLocalLoader import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.tokens.UserTokensRepository import com.tangem.tap.domain.walletconnect.WalletConnectRepository import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.persistence.PreferencesStorage @@ -39,16 +40,14 @@ val store = Store( state = AppState(), ) val logConfig = LogConfig() - lateinit var foregroundActivityObserver: ForegroundActivityObserver - lateinit var preferencesStorage: PreferencesStorage -lateinit var currenciesRepository: CurrenciesRepository lateinit var walletConnectRepository: WalletConnectRepository lateinit var shopService: TangemShopService +lateinit var assetReader: AssetReader +lateinit var userTokensRepository: UserTokensRepository class TapApplication : Application(), ImageLoaderFactory { - override fun onCreate() { super.onCreate() @@ -62,14 +61,19 @@ class TapApplication : Application(), ImageLoaderFactory { DomainLayer.init() NetworkConnectivity.createInstance(store, this) preferencesStorage = PreferencesStorage(this) - currenciesRepository = CurrenciesRepository(this, store.state.domainNetworks.tangemTechService) walletConnectRepository = WalletConnectRepository(this) - val configLoader = FeaturesLocalLoader(AndroidAssetReader(this), MoshiConverter.defaultMoshi()) + assetReader = AndroidAssetReader(this) + val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.defaultMoshi()) initConfigManager(configLoader, ::initWithConfigDependency) initWarningMessagesManager() BlockchainSdkRetrofitBuilder.enableNetworkLogging = BuildConfig.DEBUG + + userTokensRepository = UserTokensRepository.init( + context = this, + tangemTechService = store.state.domainNetworks.tangemTechService, + ) } override fun newImageLoader(): ImageLoader { diff --git a/app/src/main/java/com/tangem/tap/common/FileReader.kt b/app/src/main/java/com/tangem/tap/common/FileReader.kt new file mode 100644 index 0000000000..49e08b5bcb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/FileReader.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.common + +import android.content.Context +import com.tangem.tap.common.extensions.readFile +import com.tangem.tap.common.extensions.rewriteFile + +interface FileReader { + fun readFile(fileName: String): String + fun rewriteFile(content: String, fileName: String) +} + +class AndroidFileReader(private val context: Context) : FileReader { + override fun readFile(fileName: String): String { + return context.readFile(fileName) + } + + override fun rewriteFile(content: String, fileName: String) { + context.rewriteFile(content, fileName) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 4d227fcd92..bc9d9399dc 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -11,7 +11,6 @@ import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState -import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction @@ -24,6 +23,7 @@ import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager +import com.tangem.tap.userTokensRepository import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.DispatchFunction @@ -134,7 +134,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di scope.launch { val result = tangemSdkManager.scanProduct( store.state.globalState.analyticsHandlers, - currenciesRepository, + userTokensRepository, action.additionalBlockchainsToDerive, action.messageResId, ) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 4833079ad0..34e58f0ba7 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -32,7 +32,7 @@ import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask -import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.tokens.UserTokensRepository import com.tangem.tap.features.demo.DemoHelper import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers @@ -45,7 +45,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co suspend fun scanProduct( analyticsHandler: AnalyticsHandler?, - currenciesRepository: CurrenciesRepository, + userTokensRepository: UserTokensRepository, additionalBlockchainsToDerive: Collection? = null, messageRes: Int? = null, ): CompletionResult { @@ -53,8 +53,8 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) return runTaskAsyncReturnOnMain( - runnable = ScanProductTask(null, currenciesRepository, additionalBlockchainsToDerive), - cardId = null, initialMessage = message + runnable = ScanProductTask(null, userTokensRepository, additionalBlockchainsToDerive), + cardId = null, initialMessage = message, ).also { sendScanResultsToAnalytics(analyticsHandler, it) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 674487ff78..a2b994507a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain import androidx.annotation.StringRes import com.tangem.common.core.TangemError +import com.tangem.network.api.tangemTech.TangemTechError import com.tangem.wallet.R interface TapErrors @@ -66,7 +67,6 @@ sealed class TapSdkError(override val messageResId: Int?) : Throwable(), TangemE object CardNotSupportedByRelease : TapSdkError(R.string.error_update_app) } - fun TapErrors.assembleErrors(): MutableList?>> { val idList = mutableListOf?>>() when (this) { @@ -74,4 +74,13 @@ fun TapErrors.assembleErrors(): MutableList?>> { is TapError -> idList.add(Pair(this.messageResource, this.args)) } return idList -} \ No newline at end of file +} + +fun TangemTechError.toTapError(): TapError { + return when (this.code) { + 404 -> NoDataError(this.description) + else -> TapError.CustomError(customMessage = this.description) + } +} + +class NoDataError(message: String) : TapError.CustomError(customMessage = message) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 55e86e7368..4f64bc1d20 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -16,7 +16,6 @@ import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.isMultiwalletAllowed import com.tangem.tap.domain.extensions.makePrimaryWalletManager @@ -24,23 +23,21 @@ import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.wallet.models.toBlockchainNetworks import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.store +import com.tangem.tap.userTokensRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext - class TapWalletManager { val walletManagerFactory: WalletManagerFactory - by lazy { WalletManagerFactory(blockchainSdkConfig) } - + by lazy { WalletManagerFactory(blockchainSdkConfig) } val rates: RatesRepository = RatesRepository() - private val blockchainSdkConfig by lazy { store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig() } - private val walletManagersThrottler = ThrottlerWithValues>(10000) @@ -65,16 +62,16 @@ class TapWalletManager { WalletAction.LoadWallet.NoAccount( walletManager.wallet, blockchainNetwork, - (result.error as TapError.WalletManager.NoAccountError).customMessage - ) + (result.error as TapError.WalletManager.NoAccountError).customMessage, + ), ) } else -> { dispatchOnMain( WalletAction.LoadWallet.Failure( walletManager.wallet, - result.error.localizedMessage - ) + result.error.localizedMessage, + ), ) } } @@ -139,22 +136,52 @@ class TapWalletManager { } private suspend fun loadMultiWalletData( - scanResponse: ScanResponse + scanResponse: ScanResponse, ) { - val savedCurrencies = currenciesRepository.loadSavedCurrencies( - scanResponse.card.cardId, scanResponse.card.settings.isHDWalletAllowed - ) - if (savedCurrencies.isEmpty()) return + when (val tokensResult = userTokensRepository.getUserTokens(scanResponse.card)) { + is Result.Success -> { + withMainContext { + val blockchainNetworks = tokensResult.data.toBlockchainNetworks() + val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, tokensResult.data) + store.dispatch( + WalletAction.MultiWallet.AddBlockchains( + blockchains = blockchainNetworks, + walletManagers = walletManagers, + save = false, + ), + ) - val walletManagers = - walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies) - dispatchOnMain( - WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers), - ) - savedCurrencies.map { - if (it.tokens.isNotEmpty()) { - dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it)) + blockchainNetworks.filter { it.tokens.isNotEmpty() } + .map { + store.dispatch( + WalletAction.MultiWallet.AddTokens( + tokens = it.tokens, + blockchain = it, + save = false, + ), + ) + } + checkIfDerivationsAreMissing(blockchainNetworks, scanResponse) + } } + is Result.Failure -> { + return + } + } + } + + private fun checkIfDerivationsAreMissing(blockchainNetworks: List, scanResponse: ScanResponse) { + blockchainNetworks.map { + if (it.tokens.isNotEmpty()) { + WalletAction.MultiWallet.AddTokens(it.tokens, it, false) + } + } + val missingDerivations = blockchainNetworks + .filter { + it.derivationPath != null && !scanResponse.hasDerivation(it.blockchain, it.derivationPath) + } + if (missingDerivations.isNotEmpty()) { + store.dispatch(WalletAction.MultiWallet.AddMissingDerivations(missingDerivations)) } } @@ -172,9 +199,10 @@ class TapWalletManager { } dispatchOnMain( WalletAction.MultiWallet.AddBlockchains( - listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)), - listOf(primaryWalletManager) - ) + blockchains = listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)), + walletManagers = listOf(primaryWalletManager), + save = false, + ), ) } } diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index 34b8289884..90981e2359 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -1,6 +1,10 @@ package com.tangem.tap.domain.extensions -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationParams +import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.card.Card import com.tangem.common.card.CardWallet import com.tangem.common.card.EllipticCurve @@ -99,9 +103,11 @@ fun WalletManagerFactory.makeWalletManagerForApp( } fun WalletManagerFactory.makeWalletManagersForApp( - scanResponse: ScanResponse, blockchains: List, + scanResponse: ScanResponse, blockchains: List, ): List { - return blockchains.mapNotNull { this.makeWalletManagerForApp(scanResponse, it) } + return blockchains + .filter { it.isBlockchain() } + .mapNotNull { this.makeWalletManagerForApp(scanResponse, it) } } fun WalletManagerFactory.makePrimaryWalletManager( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index e54c3744cb..485ee0eaea 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -32,16 +32,14 @@ import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.extensions.getPrimaryCurve import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.tokens.UserTokensRepository import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.preferencesStorage -import com.tangem.tap.scope -import kotlinx.coroutines.launch class ScanProductTask( val card: Card? = null, - private val currenciesRepository: CurrenciesRepository?, - private val additionalBlockchainsToDerive: Collection? = null + private val userTokensRepository: UserTokensRepository?, + private val additionalBlockchainsToDerive: Collection? = null, ) : CardSessionRunnable { override fun run( @@ -62,7 +60,7 @@ class ScanProductTask( val commandProcessor = when { card.isTangemNote() -> ScanNoteProcessor() card.isTangemTwins() -> ScanTwinProcessor() - else -> ScanWalletProcessor(currenciesRepository, additionalBlockchainsToDerive) + else -> ScanWalletProcessor(userTokensRepository, additionalBlockchainsToDerive) } commandProcessor.proceed(card, session) { processorResult -> when (processorResult) { @@ -111,8 +109,8 @@ private class ScanNoteProcessor : ProductCommandProcessor { } private class ScanWalletProcessor( - private val currenciesRepository: CurrenciesRepository?, - private val additionalBlockchainsToDerive: Collection? = null + private val userTokensRepository: UserTokensRepository?, + private val additionalBlockchainsToDerive: Collection? = null, ) : ProductCommandProcessor { var primaryCard: PrimaryCard? = null @@ -184,47 +182,41 @@ private class ScanWalletProcessor( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - scope.launch { - val derivations = collectDerivations(card) - if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { - callback( - CompletionResult.Success( - ScanResponse( - card = card, - productType = ProductType.Wallet, - walletData = session.environment.walletData, - primaryCard = primaryCard - ) - ) - ) - return@launch - } + val derivations = collectDerivations(card) + if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { + callback( + CompletionResult.Success( + ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + primaryCard = primaryCard, + ), + ), + ) + return + } - DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> - when (result) { - is CompletionResult.Success -> { - val response = ScanResponse( - card = card, - productType = ProductType.Wallet, - walletData = session.environment.walletData, - derivedKeys = result.data.entries, - primaryCard = primaryCard - ) - callback(CompletionResult.Success(response)) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + val response = ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + derivedKeys = result.data.entries, + primaryCard = primaryCard, + ) + callback(CompletionResult.Success(response)) } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } - private suspend fun getBlockchainsToDerive(card: Card): List { - val currenciesRepository = currenciesRepository ?: return emptyList() - - val cardCurrencies = currenciesRepository - .loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed).toMutableList() - - val blockchainsToDerive = cardCurrencies.ifEmpty { + private fun getBlockchainsToDerive(card: Card): List { + val userTokensRepository = userTokensRepository ?: return emptyList() + val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(card).toMutableList().ifEmpty { mutableListOf( BlockchainNetwork(Blockchain.Bitcoin, card), BlockchainNetwork(Blockchain.Ethereum, card), @@ -236,7 +228,7 @@ private class ScanWalletProcessor( listOf( BlockchainNetwork(Blockchain.Ethereum, card), BlockchainNetwork(Blockchain.EthereumTestnet, card), - ) + ), ) } if (additionalBlockchainsToDerive != null) { @@ -256,16 +248,14 @@ private class ScanWalletProcessor( return blockchainsToDerive.distinct() } - private suspend fun collectDerivations(card: Card): Map> { + private fun collectDerivations(card: Card): Map> { val blockchains = getBlockchainsToDerive(card) val derivations = mutableMapOf>() blockchains.forEach { blockchain -> val curve = blockchain.blockchain.getPrimaryCurve() - val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach if (wallet.chainCode == null) return@forEach - val key = wallet.publicKey.toMapKey() val path = blockchain.derivationPath?.let { DerivationPath(it) } if (path != null) { diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index 2dafa32075..d3d5836701 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -1,247 +1,12 @@ package com.tangem.tap.domain.tokens -import android.app.Application -import android.content.Context -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle -import com.tangem.blockchain.common.Token import com.tangem.common.card.FirmwareVersion -import com.tangem.common.services.Result -import com.tangem.domain.common.extensions.getTokens -import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.network.api.tangemTech.TangemTechService -import com.tangem.network.common.MoshiConverter -import com.tangem.tap.common.extensions.appendIf -import com.tangem.tap.common.extensions.readJsonFileToString -import com.tangem.tap.domain.tokens.models.BlockchainNetwork -import com.tangem.tap.domain.tokens.models.ObsoleteTokenDao -import com.tangem.tap.domain.tokens.models.TokenDao -import com.tangem.tap.features.demo.DemoHelper -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import timber.log.Timber -import java.util.* - -class CurrenciesRepository( - private val context: Application, - private val tangemNetworkService: TangemTechService -) { - - private val moshi = MoshiConverter.defaultMoshi() - private val blockchainsAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(List::class.java, Blockchain::class.java) - ) - private val tokensAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(List::class.java, TokenDao::class.java) - ) - private val obsoleteTokensAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(List::class.java, ObsoleteTokenDao::class.java) - ) - private val currenciesAdapter: JsonAdapter = - moshi.adapter(CurrenciesFromJson::class.java) - - private val blockchainNetworkAdapter: JsonAdapter> = - moshi.adapter(Types.newParameterizedType(List::class.java, BlockchainNetwork::class.java)) - - fun saveUpdatedCurrency(cardId: String, blockchainNetwork: BlockchainNetwork) { - var changed = false - val currencies = loadSavedCurrenciesWithoutMigration(cardId).map { - if (it == blockchainNetwork) { - changed = true - blockchainNetwork - } else { - it - } - } - val updatedCurrencies = if (changed) currencies else currencies + blockchainNetwork - saveCurrencies(cardId, updatedCurrencies.distinct()) - } - - fun removeToken(cardId: String, token: Token, blockchainNetwork: BlockchainNetwork) { - val currencies = loadSavedCurrenciesWithoutMigration(cardId).map { - if (it == blockchainNetwork) { - it.copy(tokens = it.tokens.filterNot { it == token }) - } else { - it - } - } - saveCurrencies(cardId, currencies) - } - - fun removeBlockchain(cardId: String, blockchainNetwork: BlockchainNetwork) { - val currencies = loadSavedCurrenciesWithoutMigration(cardId) - .filterNot { it == blockchainNetwork } - saveCurrencies(cardId, currencies) - } - - fun removeCurrencies(cardId: String) { - saveCurrencies(cardId, emptyList()) - } - - @Deprecated("Use BlockchainNetwork instead") - private fun loadSavedTokens(cardId: String): List { - val json = try { - context.readFileText(getFileNameForTokens(cardId)) - } catch (exception: Exception) { - return emptyList() - } - - return try { - tokensAdapter.fromJson(json) ?: emptyList() - } catch (exception: Exception) { - emptyList() - } - } - - @Deprecated("Use BlockchainNetwork instead") - private fun loadSavedBlockchains(cardId: String): List { - return try { - val json = context.readFileText(getFileNameForBlockchains(cardId)) - blockchainsAdapter.fromJson(json)?.distinct() ?: emptyList() - } catch (exception: Exception) { - emptyList() - } - } - - suspend fun loadSavedCurrencies( - cardId: String, - isHdWalletSupported: Boolean = false - ): List { - if (DemoHelper.isDemoCardId(cardId)) { - return loadDemoCurrencies() - } - return try { - val json = context.readFileText(getFileNameForBlockchains(cardId)) - blockchainNetworkAdapter.fromJson(json)?.distinct() ?: emptyList() - } catch (exception: Exception) { - tryToLoadPreviousFormatAndMigrate(cardId, isHdWalletSupported) - } - } - - fun loadSavedCurrenciesWithoutMigration( - cardId: String, - ): List { - if (DemoHelper.isDemoCardId(cardId)) { - return loadDemoCurrencies() - } - return try { - val json = context.readFileText(getFileNameForBlockchains(cardId)) - blockchainNetworkAdapter.fromJson(json)?.distinct() ?: emptyList() - } catch (exception: Exception) { - emptyList() - } - } - - private fun loadDemoCurrencies(): List { - return DemoHelper.config.demoBlockchains.map { - BlockchainNetwork( - blockchain = it, - derivationPath = it.derivationPath(DerivationStyle.LEGACY)?.rawPath, - tokens = emptyList() - ) - } - } - - private suspend fun tryToLoadPreviousFormatAndMigrate( - cardId: String, - isHdWalletSupported: Boolean = false - ): List { - return try { - loadSavedCurrenciesOldWay( - cardId, - isHdWalletSupported - ) - } catch (exception: Exception) { - emptyList() - } - } - - private suspend fun loadSavedCurrenciesOldWay( - cardId: String, isHdWalletSupported: Boolean = false - ): List { - val blockchains = loadSavedBlockchains(cardId) - val tokens = loadSavedTokens(cardId) - val ids = getTokensIds(tokens) - val derivationStyle = if (isHdWalletSupported) DerivationStyle.LEGACY else null - val blockchainNetworks = blockchains.map { blockchain -> - BlockchainNetwork( - blockchain = blockchain, - derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath, - tokens = tokens - .filter { it.blockchainDao.toBlockchain() == blockchain } - .map { - val token = it.toToken() - token.copy(id = ids[token.contractAddress]) - } - ) - } - saveCurrencies(cardId, blockchainNetworks) // migrate saved currencies - return blockchainNetworks - } - - private suspend fun getTokensIds(tokens: List): Map = coroutineScope { - tokens.map { - async { - tangemNetworkService.getTokens( - contractAddress = it.contractAddress, - networkId = it.blockchainDao.toBlockchain().toNetworkId(), - active = true, - ) - } - }.map { it.await() } - .map { (it as? Result.Success)?.data?.coins?.firstOrNull()?.id } - .mapIndexedNotNull { index, id -> - if (id == null) null else tokens[index].contractAddress to id - }.toMap() - } - - fun saveCurrencies(cardId: String, currencies: List) { - val json = blockchainNetworkAdapter.toJson(currencies) - context.rewriteFile(json, getFileNameForBlockchains(cardId)) - } - - private fun Context.readFileText(fileName: String): String = - this.openFileInput(fileName).bufferedReader().readText() - - private fun Context.rewriteFile(content: String, fileName: String) { - this.openFileOutput(fileName, Context.MODE_PRIVATE).use { - it.write(content.toByteArray(), 0, content.length) - } - } - - fun getTestnetCoins(): List { - val json = context.assets.readJsonFileToString(FILE_NAME_TESTNET_COINS) - return currenciesAdapter.fromJson(json)!!.coins - .map { Currency.fromJsonObject(it) } - } - - private fun loadTokensJson(blockchain: Blockchain): String? { - val fileName = getFileName(blockchain) - return try { - context.assets.readJsonFileToString(fileName) - } catch (ex: Exception) { - Timber.e(ex, "Tokens with the file name %s not found", fileName) - null - } - } - - private fun getFileName(blockchain: Blockchain): String { - return StringBuilder().apply { - append(blockchain.id.lowercase(Locale.getDefault()).replace("/test", "")) - append("_tokens") - appendIf("_testnet") { blockchain.isTestnet() } - }.toString() - } - - private fun fromJsonToTokensDao(tokenJson: String, blockchain: Blockchain): List { - return obsoleteTokensAdapter.fromJson(tokenJson)!!.map { it.toTokenDao(blockchain) } - } +object CurrenciesRepository { fun getBlockchains( cardFirmware: FirmwareVersion, - isTestNet: Boolean = false + isTestNet: Boolean = false, ): List { val blockchains = if (cardFirmware < FirmwareVersion.MultiWalletAvailable) { Blockchain.secp256k1Blockchains(isTestNet) @@ -261,21 +26,6 @@ class CurrenciesRepository( ) } } - - companion object { - private const val FILE_NAME_PREFIX_TOKENS = "tokens" - private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" - private const val FILE_NAME_TESTNET_COINS = "testnet_tokens" - - fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" - fun getFileNameForBlockchains(cardId: String): String = - "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" - } } -fun Blockchain.getTokensName(): String { - return when (this) { - Blockchain.Fantom -> "Fantom Opera" - else -> this.fullName - } -} + diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt b/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt index 5b806beecc..7df68e80c3 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt @@ -1,29 +1,36 @@ package com.tangem.tap.domain.tokens +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi import com.tangem.blockchain.common.Blockchain import com.tangem.common.services.Result import com.tangem.domain.common.extensions.getListOfCoins import com.tangem.domain.common.extensions.toNetworkId import com.tangem.network.api.tangemTech.CoinsResponse import com.tangem.network.api.tangemTech.TangemTechService +import com.tangem.network.common.MoshiConverter +import com.tangem.tap.common.AssetReader class LoadAvailableCoinsService( private val networkService: TangemTechService, - private val currenciesRepository: CurrenciesRepository + private val assetReader: AssetReader, ) { + private val moshi: Moshi by lazy { MoshiConverter.defaultMoshi() } + private val currenciesAdapter: JsonAdapter = + moshi.adapter(CurrenciesFromJson::class.java) suspend fun getSupportedTokens( isTestNet: Boolean = false, supportedBlockchains: List, page: Int, - searchInput: String? = null + searchInput: String? = null, ): Result { if (isTestNet) { return Result.Success( LoadedCoins( - currencies = currenciesRepository.getTestnetCoins().filter(searchInput), + currencies = getTestnetCoins().filter(searchInput), moreAvailable = false, - ) + ), ) } val offset = page * LOAD_PER_PAGE @@ -56,21 +63,28 @@ class LoadAvailableCoinsService( active = true, offset = offset, limit = LOAD_PER_PAGE, - searchText = searchInput + searchText = searchInput, ) } + fun getTestnetCoins(): List { + val json = assetReader.readAssetAsString(FILE_NAME_TESTNET_COINS) + return currenciesAdapter.fromJson(json)!!.coins + .map { Currency.fromJsonObject(it) } + } + private fun List.filter(searchInput: String?): List { if (searchInput.isNullOrBlank()) return this - return filter{ + return filter { it.symbol.contains(searchInput, ignoreCase = true) || - it.name.contains(searchInput, ignoreCase = true) + it.name.contains(searchInput, ignoreCase = true) } } companion object { const val LOAD_PER_PAGE = 100 + private const val FILE_NAME_TESTNET_COINS = "testnet_tokens" } } diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/OldUserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/OldUserTokensRepository.kt new file mode 100644 index 0000000000..7418891662 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/OldUserTokensRepository.kt @@ -0,0 +1,130 @@ +package com.tangem.tap.domain.tokens + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Types +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationStyle +import com.tangem.common.services.Result +import com.tangem.domain.common.extensions.getTokens +import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.network.api.tangemTech.TangemTechService +import com.tangem.network.common.MoshiConverter +import com.tangem.tap.common.FileReader +import com.tangem.tap.domain.tokens.models.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.TokenDao +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +@Deprecated("Use this only for migration") +class OldUserTokensRepository( + private val fileReader: FileReader, + private val tangemNetworkService: TangemTechService, +) { + private val moshi = MoshiConverter.defaultMoshi() + private val blockchainsAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, Blockchain::class.java), + ) + private val tokensAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, TokenDao::class.java), + ) + private val blockchainNetworkAdapter: JsonAdapter> = + moshi.adapter(Types.newParameterizedType(List::class.java, BlockchainNetwork::class.java)) + + @Deprecated("Use BlockchainNetwork instead") + private fun loadSavedTokens(cardId: String): List { + val json = try { + fileReader.readFile(getFileNameForTokens(cardId)) + } catch (exception: Exception) { + return emptyList() + } + + return try { + tokensAdapter.fromJson(json) ?: emptyList() + } catch (exception: Exception) { + emptyList() + } + } + + @Deprecated("Use BlockchainNetwork instead") + private fun loadSavedBlockchains(cardId: String): List { + return try { + val json = fileReader.readFile(getFileNameForBlockchains(cardId)) + blockchainsAdapter.fromJson(json)?.distinct() ?: emptyList() + } catch (exception: Exception) { + emptyList() + } + } + + @Deprecated("Use TokensRepository instead") + suspend fun loadSavedCurrencies( + cardId: String, + isHdWalletSupported: Boolean = false, + ): List { + return try { + val json = fileReader.readFile(getFileNameForBlockchains(cardId)) + blockchainNetworkAdapter.fromJson(json)?.distinct() ?: emptyList() + } catch (exception: Exception) { + tryToLoadPreviousFormatAndMigrate(cardId, isHdWalletSupported) + } + } + + private suspend fun tryToLoadPreviousFormatAndMigrate( + cardId: String, + isHdWalletSupported: Boolean = false, + ): List { + return try { + loadSavedCurrenciesOldWay( + cardId, + isHdWalletSupported, + ) + } catch (exception: Exception) { + emptyList() + } + } + + private suspend fun loadSavedCurrenciesOldWay( + cardId: String, isHdWalletSupported: Boolean = false, + ): List { + val blockchains = loadSavedBlockchains(cardId) + val tokens = loadSavedTokens(cardId) + val ids = getTokensIds(tokens) + val derivationStyle = if (isHdWalletSupported) DerivationStyle.LEGACY else null + val blockchainNetworks = blockchains.map { blockchain -> + BlockchainNetwork( + blockchain = blockchain, + derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath, + tokens = tokens + .filter { it.blockchainDao.toBlockchain() == blockchain } + .map { + val token = it.toToken() + token.copy(id = ids[token.contractAddress]) + }, + ) + } + return blockchainNetworks + } + + private suspend fun getTokensIds(tokens: List): Map = coroutineScope { + tokens.map { + async { + tangemNetworkService.getTokens( + contractAddress = it.contractAddress, + networkId = it.blockchainDao.toBlockchain().toNetworkId(), + active = true, + ) + } + }.map { it.await() } + .map { (it as? Result.Success)?.data?.coins?.firstOrNull()?.id } + .mapIndexedNotNull { index, id -> + if (id == null) null else tokens[index].contractAddress to id + }.toMap() + } + + companion object { + private const val FILE_NAME_PREFIX_TOKENS = "tokens" + private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" + private fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" + private fun getFileNameForBlockchains(cardId: String): String = + "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensNetworkService.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensNetworkService.kt new file mode 100644 index 0000000000..ad188c6913 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensNetworkService.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.domain.tokens + +import com.tangem.common.core.TangemSdkError +import com.tangem.common.services.Result +import com.tangem.network.api.tangemTech.TangemTechService +import com.tangem.network.api.tangemTech.UserTokensResponse +import com.tangem.tap.domain.NoDataError +import com.tangem.tap.features.wallet.models.Currency + +class UserTokensNetworkService(private val tangemTechService: TangemTechService) { + suspend fun getUserTokens(userId: String): Result { + return when (val result = tangemTechService.getUserTokens(userId)) { + is Result.Success -> result + is Result.Failure -> { + val error = result.error + if (error is TangemSdkError.NetworkError && error.customMessage.contains("404")) { + return Result.Failure(NoDataError(error.customMessage)) + } else { + return result + } + } + } + } + + suspend fun saveUserTokens(userId: String, tokens: List): Result { + val tokensResponse = tokens.map { it.toTokenResponse() } + val data = UserTokensResponse(tokens = tokensResponse) + return tangemTechService.putUserTokens(userId, data) + } +} diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt new file mode 100644 index 0000000000..58c3fb08a4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -0,0 +1,117 @@ +package com.tangem.tap.domain.tokens + +import android.content.Context +import com.tangem.blockchain.common.DerivationStyle +import com.tangem.common.card.Card +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.toHexString +import com.tangem.common.services.Result +import com.tangem.domain.common.extensions.calculateHmacSha256 +import com.tangem.network.api.tangemTech.TangemTechService +import com.tangem.tap.common.AndroidFileReader +import com.tangem.tap.domain.NoDataError +import com.tangem.tap.domain.tokens.models.BlockchainNetwork +import com.tangem.tap.features.demo.DemoHelper +import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.features.wallet.models.toBlockchainNetworks +import com.tangem.tap.features.wallet.models.toCurrencies +import com.tangem.tap.network.NetworkConnectivity +import com.tangem.tap.store +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch + +class UserTokensRepository( + private val storageService: UserTokensStorageService, + private val networkService: UserTokensNetworkService, +) { + suspend fun getUserTokens(card: Card): Result> { + if (DemoHelper.isDemoCardId(card.cardId)) { + return Result.Success(loadDemoCurrencies()) + } + val userId = card.getUserId() + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + return Result.Success(loadTokensOffline(card, userId)) + } + + return when (val networkResult = networkService.getUserTokens(userId)) { + is Result.Success -> { + val tokens = networkResult.data.tokens.map { Currency.fromTokenResponse(it) } + storageService.saveUserTokens(card.getUserId(), tokens) + Result.Success(tokens) + } + is Result.Failure -> { + handleGetUserTokensFailure(card = card, userId = userId, error = networkResult.error) + } + } + } + + suspend fun saveUserTokens(card: Card, tokens: List) { + networkService.saveUserTokens(card.getUserId(), tokens) + storageService.saveUserTokens(card.getUserId(), tokens) + } + + suspend fun removeUserTokens(card: Card) { + networkService.saveUserTokens(card.getUserId(), emptyList()) + storageService.saveUserTokens(card.getUserId(), emptyList()) + } + + fun loadBlockchainsToDerive(card: Card): List { + return storageService.getUserTokens(card.getUserId())?.toBlockchainNetworks() ?: emptyList() + } + + private fun loadDemoCurrencies(): List { + return DemoHelper.config.demoBlockchains.map { + BlockchainNetwork( + blockchain = it, + derivationPath = it.derivationPath(DerivationStyle.LEGACY)?.rawPath, + tokens = emptyList(), + ) + }.flatMap { it.toCurrencies() } + } + + private suspend fun handleGetUserTokensFailure( + card: Card, + userId: String, + error: Throwable, + ): Result> { + return when (error) { + is NoDataError -> { + val tokens = storageService.getUserTokens(card) + coroutineScope { launch { networkService.saveUserTokens(userId = userId, tokens = tokens) } } + Result.Success(tokens) + } + else -> { + val tokens = storageService.getUserTokens(userId) ?: storageService.getUserTokens(card) + Result.Success(tokens) + } + } + } + + private suspend fun loadTokensOffline(card: Card, userId: String): List { + return storageService.getUserTokens(userId) ?: storageService.getUserTokens(card) + } + + private fun Card.getUserId(): String { + val walletPublicKey = this.wallets.firstOrNull()?.publicKey ?: return "" + return calculateUserId(walletPublicKey) + } + + private fun calculateUserId(walletPublicKey: ByteArray): String { + val message = MESSAGE.toByteArray() + val keyHash = walletPublicKey.calculateSha256() + return message.calculateHmacSha256(keyHash).toHexString() + } + + companion object { + const val MESSAGE = "AccountID" + fun init(context: Context, tangemTechService: TangemTechService): UserTokensRepository { + val fileReader = AndroidFileReader(context) + val oldUserTokensRepository = OldUserTokensRepository( + fileReader, store.state.domainNetworks.tangemTechService, + ) + val storageService = UserTokensStorageService(oldUserTokensRepository, fileReader) + val networkService = UserTokensNetworkService(tangemTechService) + return UserTokensRepository(storageService, networkService) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt new file mode 100644 index 0000000000..f2011cbebb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt @@ -0,0 +1,48 @@ +package com.tangem.tap.domain.tokens + +import com.squareup.moshi.JsonAdapter +import com.tangem.Log +import com.tangem.common.card.Card +import com.tangem.network.api.tangemTech.UserTokensResponse +import com.tangem.network.common.MoshiConverter +import com.tangem.tap.common.FileReader +import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.features.wallet.models.toCurrencies + +class UserTokensStorageService( + private val oldUserTokensRepository: OldUserTokensRepository, + private val fileReader: FileReader, +) { + private val moshi = MoshiConverter.defaultMoshi() + private val userTokensAdapter: JsonAdapter = + moshi.adapter(UserTokensResponse::class.java) + + fun getUserTokens(userId: String): List? { + return try { + val json = fileReader.readFile(getFileNameForUserTokens(userId)) + userTokensAdapter.fromJson(json)?.tokens?.map { Currency.fromTokenResponse(it) } + } catch (exception: Exception) { + Log.error { exception.stackTraceToString() } + null + } + } + + @Deprecated("") + suspend fun getUserTokens(card: Card): List { + val blockchainNetworks = + oldUserTokensRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) + return blockchainNetworks.flatMap { it.toCurrencies() } + } + + fun saveUserTokens(userId: String, tokens: List) { + val tokensResponse = tokens.map { it.toTokenResponse() } + val data = UserTokensResponse(tokens = tokensResponse) + val json = userTokensAdapter.toJson(data) + fileReader.rewriteFile(json, getFileNameForUserTokens(userId)) + } + + companion object { + private const val FILE_NAME_PREFIX_USER_TOKENS = "user_tokens" + private fun getFileNameForUserTokens(userId: String): String = "${FILE_NAME_PREFIX_USER_TOKENS}_$userId" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt deleted file mode 100644 index 144f3b873d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.tangem.tap.domain.walletconnect - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.domain.common.ScanResponse -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.tap.domain.extensions.makeWalletManagerForApp -import com.tangem.tap.domain.tokens.CurrenciesRepository -import com.tangem.tap.domain.tokens.models.BlockchainNetwork -import com.tangem.tap.features.details.redux.walletconnect.WalletForSession -import com.tangem.tap.features.wallet.redux.WalletState - -class WcWalletManagerFactory( - private val factory: WalletManagerFactory, - private val currenciesRepository: CurrenciesRepository, -) { - fun getWalletManager( - wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState, - ): WalletManager? { - val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) { - Blockchain.EthereumTestnet - } else { - blockchain - } - val blockchainNetwork = BlockchainNetwork( - blockchain = blockchainToMake, - derivationPath = wallet.derivationPath?.rawPath, - tokens = emptyList(), - ) - return walletState.getWalletManager(blockchainNetwork) - } - - suspend fun getWalletManager( - scanResponse: ScanResponse, blockchain: Blockchain, walletState: WalletState, - ): WalletManager? { - val card = scanResponse.card - val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) { - Blockchain.EthereumTestnet - } else { - blockchain - } - val blockchainNetwork = BlockchainNetwork( - blockchain = blockchainToMake, - card = card, - ) - - return if (walletState.cardId == card.cardId) { - walletState.getWalletManager(blockchainNetwork) - } else { - if (currenciesRepository - .loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) - .contains(blockchainNetwork) - ) { - factory.makeWalletManagerForApp( - scanResponse = scanResponse, - blockchainNetwork = blockchainNetwork, - ) - } else { - null - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 7825d58a8f..0351eb207b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -11,7 +11,6 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.currenciesRepository import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction @@ -108,11 +107,9 @@ class DetailsMiddleware { } scope.launch { val result = tangemSdkManager.resetToFactorySettings(card) - withContext(Dispatchers.Main) { when (result) { is CompletionResult.Success -> { - currenciesRepository.removeCurrencies(card.cardId) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home)) } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> @@ -123,7 +120,6 @@ class DetailsMiddleware { ) } } - } } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 8fdcfa10ce..9a898f708d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.WalletManager import com.tangem.common.card.Card import com.tangem.common.extensions.guard import com.tangem.domain.common.ScanResponse @@ -12,12 +13,11 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.extensions.isMultiwalletAllowed +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.walletconnect.BnbHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils -import com.tangem.tap.domain.walletconnect.WcWalletManagerFactory import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.scope @@ -189,15 +189,10 @@ class WalletConnectMiddleware { store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork)) return } - val factory = WcWalletManagerFactory( - factory = store.state.globalState.tapWalletManager.walletManagerFactory, - currenciesRepository = currenciesRepository, - ) - val walletState = store.state.walletState - val walletManager = factory.getWalletManager( + val walletManager = getWalletManager( wallet = action.session.wallet, blockchain = blockchain, - walletState = walletState, + walletState = store.state.walletState, ).guard { store.dispatchOnMain( GlobalAction.ShowDialog( @@ -230,20 +225,10 @@ class WalletConnectMiddleware { handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain) } - private suspend fun getAvailableBlockchains(card: Card, walletState: WalletState): List { - return if (walletState.cardId == card.cardId) { - walletState.currencies.filter { - it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm() - }.map { it.blockchain } - } else { - currenciesRepository - .loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) - .filter { - it.blockchain.isEvm() && - it.derivationPath == it.blockchain.derivationPath(card.derivationStyle)?.rawPath - } - .map { it.blockchain } - } + private fun getAvailableBlockchains(card: Card, walletState: WalletState): List { + return walletState.currencies.filter { + it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm() + }.map { it.blockchain } } private suspend fun prepareWalletManager( @@ -253,11 +238,7 @@ class WalletConnectMiddleware { session: WalletConnectSession, walletConnectManager: WalletConnectManager, ) { - val factory = WcWalletManagerFactory( - factory = store.state.globalState.tapWalletManager.walletManagerFactory, - currenciesRepository = currenciesRepository, - ) - val walletManager = factory.getWalletManager(scanResponse, blockchain, walletState).guard { + val walletManager = getWalletManager(session.wallet, blockchain, walletState).guard { store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session)) store.dispatchOnMain( GlobalAction.ShowDialog( @@ -306,17 +287,27 @@ class WalletConnectMiddleware { NewWcSessionData(session = updatedSession, scanResponse = scanResponse, blockchain = blockchain), ), ) + val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList() + store.dispatch( + GlobalAction.ShowDialog( + WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains), + ), + ) + } - scope.launch { - val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList() - - withMainContext { - store.dispatch( - GlobalAction.ShowDialog( - WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains), - ), - ) - } + private fun getWalletManager( + wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState, + ): WalletManager? { + val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) { + Blockchain.EthereumTestnet + } else { + blockchain } + val blockchainNetwork = BlockchainNetwork( + blockchain = blockchainToMake, + derivationPath = wallet.derivationPath?.rawPath, + tokens = emptyList(), + ) + return walletState.getWalletManager(blockchainNetwork) } } \ No newline at end of file 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 374b8d4406..6b0bf8e8e0 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 @@ -78,30 +78,35 @@ private fun handleReadCard() { store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) } else { changeButtonState(ButtonState.PROGRESS) - store.dispatch(GlobalAction.ScanCard(onSuccess = { scanResponse -> - store.state.globalState.tapWalletManager.updateConfigManager(scanResponse) - store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse)) + store.dispatch( + GlobalAction.ScanCard( + onSuccess = { scanResponse -> + store.state.globalState.tapWalletManager.updateConfigManager(scanResponse) + store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse)) - if (OnboardingHelper.isOnboardingCase(scanResponse)) { - val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) - store.dispatch(GlobalAction.Onboarding.Start(scanResponse)) - navigateTo(navigateTo) - } else { - scope.launch { - store.onCardScanned(scanResponse) - withMainContext { - if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) { - store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly)) - navigateTo(AppScreen.OnboardingTwins) - } else { - navigateTo(AppScreen.Wallet, null) + if (OnboardingHelper.isOnboardingCase(scanResponse)) { + val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) + store.dispatch(GlobalAction.Onboarding.Start(scanResponse)) + navigateTo(navigateTo) + } else { + scope.launch { + withMainContext { + if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly)) + navigateTo(AppScreen.OnboardingTwins) + } else { + navigateTo(AppScreen.Wallet, null) + } + } + store.onCardScanned(scanResponse) } } - } - } - }, onFailure = { - changeButtonState(ButtonState.ENABLED) - })) + }, + onFailure = { + changeButtonState(ButtonState.ENABLED) + }, + ), + ) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 0dc7e10566..e7d3a6501c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -18,7 +18,6 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.makePrimaryWalletManager import com.tangem.tap.domain.twins.TwinCardsManager @@ -271,7 +270,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } CreateTwinWalletMode.RecreateWallet -> { - currenciesRepository.removeCurrencies(scanResponse.card.cardId) store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 391d65cb96..25fc8e2f38 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -94,8 +94,8 @@ private fun handleWalletAction(action: Action) { store.dispatchOnMain( WalletAction.MultiWallet.SaveCurrencies( blockchainNetworks = blockchainNetworks, - cardId = result.data.card.cardId - ) + card = result.data.card, + ), ) onboardingManager.activationStarted(updatedResponse.card.cardId) store.dispatch(OnboardingWalletAction.ProceedBackup) @@ -273,15 +273,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) backupService.proceedBackup { result -> when (result) { is CompletionResult.Success -> { - val blockchainNetworks = listOf( - BlockchainNetwork(Blockchain.Bitcoin, result.data), - BlockchainNetwork(Blockchain.Ethereum, result.data) - ) - store.dispatchOnMain( - WalletAction.MultiWallet.SaveCurrencies( - blockchainNetworks = blockchainNetworks, cardId = result.data.cardId - ) - ) if (backupService.currentState == BackupService.State.Finished) { store.dispatchOnMain(BackupAction.FinishBackup) } else { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 2ec703e629..96f742ec97 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -19,7 +19,8 @@ import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction import com.tangem.domain.redux.domainStore import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.* +import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.assetReader import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState @@ -32,6 +33,9 @@ import com.tangem.tap.domain.tokens.LoadAvailableCoinsService import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -77,7 +81,7 @@ class TokensMiddleware { val loadCoinsService = LoadAvailableCoinsService( store.state.domainNetworks.tangemTechService, - currenciesRepository + assetReader, ) scope.launch { @@ -285,22 +289,28 @@ class TokensMiddleware { else -> DerivationParams.Custom(derivationPath) } } - val walletManager = factory.makeWalletManagerForApp( scanResponse = scanResponse, blockchain = currency.blockchain, - derivationParams = derivationParams + derivationParams = derivationParams, ) ?: return@mapNotNull null val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager) - WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, walletManager) + WalletAction.MultiWallet.AddBlockchain( + blockchain = blockchainNetwork, + walletManager = walletManager, + save = true, + ) } is Currency.Token -> { val rawDerivationPath = currency.derivationPath ?: currency.blockchain.derivationPath(derivationStyle)?.rawPath - val blockchainNetwork = - BlockchainNetwork(currency.blockchain, rawDerivationPath, emptyList()) - WalletAction.MultiWallet.AddToken(currency.token, blockchainNetwork) + BlockchainNetwork(currency.blockchain, rawDerivationPath, listOf(currency.token)) + WalletAction.MultiWallet.AddToken( + token = currency.token, + blockchain = blockchainNetwork, + save = true, + ) } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt index 75f752b278..1500a10a1e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt @@ -1,14 +1,18 @@ package com.tangem.tap.features.wallet.models +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.Token +import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toCoinId +import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.features.addCustomToken.CustomCurrency +import com.tangem.network.api.tangemTech.TokenResponse import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.tokens.redux.TokenWithBlockchain sealed interface Currency { - val coinId: String? get() = when (this) { is Blockchain -> blockchain.toCoinId() @@ -17,24 +21,28 @@ sealed interface Currency { val blockchain: com.tangem.blockchain.common.Blockchain val currencySymbol: CryptoCurrencyName val derivationPath: String? - val currencyName: String get() = when (this) { is Blockchain -> blockchain.fullName is Token -> token.name } + val decimals + get() = when (this) { + is Blockchain -> blockchain.decimals() + is Token -> token.decimals + } data class Token( val token: com.tangem.blockchain.common.Token, override val blockchain: com.tangem.blockchain.common.Blockchain, - override val derivationPath: String? + override val derivationPath: String?, ) : Currency { override val currencySymbol = token.symbol } data class Blockchain( override val blockchain: com.tangem.blockchain.common.Blockchain, - override val derivationPath: String? + override val derivationPath: String?, ) : Currency { override val currencySymbol: CryptoCurrencyName = blockchain.currency } @@ -48,24 +56,35 @@ sealed interface Currency { } fun isBlockchain(): Boolean = this is Blockchain - fun isToken(): Boolean = this is Token + fun toTokenResponse(): TokenResponse { + return TokenResponse( + id = coinId ?: "", + networkId = blockchain.toNetworkId(), + derivationPath = derivationPath ?: DERIVATION_PATH_RAW_VALUE, + name = currencyName, + symbol = currencySymbol, + decimals = decimals, + contractAddress = if (this is Token) token.contractAddress else null, + ) + } companion object { + private const val DERIVATION_PATH_RAW_VALUE = "m/44/0'/0/0" fun fromBlockchainNetwork( blockchainNetwork: BlockchainNetwork, - token: com.tangem.blockchain.common.Token? = null + token: com.tangem.blockchain.common.Token? = null, ): Currency { return if (token != null) { Token( token = token, blockchain = blockchainNetwork.blockchain, - derivationPath = blockchainNetwork.derivationPath + derivationPath = blockchainNetwork.derivationPath, ) } else { Blockchain( blockchain = blockchainNetwork.blockchain, - derivationPath = blockchainNetwork.derivationPath + derivationPath = blockchainNetwork.derivationPath, ) } } @@ -74,7 +93,7 @@ sealed interface Currency { return when (customCurrency) { is CustomCurrency.CustomBlockchain -> Blockchain( blockchain = customCurrency.network, - derivationPath = customCurrency.derivationPath?.rawPath + derivationPath = customCurrency.derivationPath?.rawPath, ) is CustomCurrency.CustomToken -> Token( token = customCurrency.token, @@ -88,8 +107,53 @@ sealed interface Currency { return Token( token = tokenWithBlockchain.token, blockchain = tokenWithBlockchain.blockchain, - derivationPath = null + derivationPath = null, ) } + + fun fromTokenResponse(tokenResponse: TokenResponse): Currency { + val derivationPath = if (tokenResponse.derivationPath == DERIVATION_PATH_RAW_VALUE) { + null + } else { + tokenResponse.derivationPath + } + return when { + tokenResponse.contractAddress != null -> Token( + com.tangem.blockchain.common.Token( + name = tokenResponse.name, + symbol = tokenResponse.symbol, + contractAddress = tokenResponse.contractAddress!!, + decimals = tokenResponse.decimals, + id = tokenResponse.id, + ), + blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenResponse.networkId)!!, + derivationPath = derivationPath, + ) + else -> Blockchain( + blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenResponse.networkId)!!, + derivationPath = derivationPath, + ) + } + } } -} \ No newline at end of file +} + +fun BlockchainNetwork.toCurrencies(): List { + val blockchain = Currency.fromBlockchainNetwork(this) + val tokens = this.tokens.map { Currency.fromBlockchainNetwork(this, it) } + return listOf(blockchain) + tokens +} + +fun List.toCurrencies(): List { + return flatMap { it.toCurrencies() } +} + +fun List.toBlockchainNetworks(): List { + return this.filter { it.isBlockchain() }.map { BlockchainNetwork(it.blockchain, it.derivationPath, getTokens(it)) } +} + +private fun List.getTokens(currency: Currency): List { + return this + .filter { it.isToken() && it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath } + .mapNotNull { if (it is Currency.Token) it.token else null } +} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 0d6e8b528a..ed617762f4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -31,7 +31,6 @@ sealed class WalletAction : Action { data class Failure(val error: TapError) : WalletAction() } - data class LoadWallet( val blockchain: BlockchainNetwork? = null, val walletManager: WalletManager? = null @@ -54,22 +53,21 @@ sealed class WalletAction : Action { data class AddBlockchain( val blockchain: BlockchainNetwork, - val walletManager: WalletManager? + val walletManager: WalletManager?, + val save: Boolean, ) : MultiWallet() data class AddBlockchains( - val blockchains: List, val walletManagers: List + val blockchains: List, val walletManagers: List, val save: Boolean, ) : MultiWallet() - data class AddTokens(val tokens: List, val blockchain: BlockchainNetwork) : + data class AddTokens(val tokens: List, val blockchain: BlockchainNetwork, val save: Boolean) : MultiWallet() - data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet() + data class AddToken(val token: Token, val blockchain: BlockchainNetwork, val save: Boolean) : MultiWallet() data class SaveCurrencies( - val blockchainNetworks: List, val cardId: String? = null + val blockchainNetworks: List, val card: Card? = null, ) : MultiWallet() -// object FindTokensInUse : MultiWallet() -// object FindBlockchainsInUse : MultiWallet() data class TokenLoaded( val amount: Amount, @@ -82,14 +80,15 @@ sealed class WalletAction : Action { data class TryToRemoveWallet(val currency: Currency) : MultiWallet() data class RemoveWallet( val currency: Currency, - val fromScreen: AppScreen + val fromScreen: AppScreen, ) : MultiWallet() data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet() data class SetPrimaryToken(val token: Token) : MultiWallet() - data class ShowWalletBackupWarning(val show: Boolean) : MultiWallet() object BackupWallet : MultiWallet() + data class AddMissingDerivations(val blockchains: List) : MultiWallet() + object ScanToGetDerivations : MultiWallet() } sealed class Warnings : WalletAction() { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 6fb9f9e73b..d6f0def028 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -19,9 +19,6 @@ import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.models.WalletRent import com.tangem.tap.features.wallet.models.WalletWarning -import com.tangem.tap.features.wallet.models.hasPendingTransactions -import com.tangem.tap.features.wallet.models.hasSendableAmounts -import com.tangem.tap.features.wallet.models.isSendableAmount import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount import com.tangem.tap.features.wallet.redux.reducers.findProgressState import com.tangem.tap.features.wallet.ui.BalanceStatus @@ -47,6 +44,7 @@ data class WalletState( val isTestnet: Boolean = false, val totalBalance: TotalBalance? = null, val showBackupWarning: Boolean = false, + val missingDerivations: List = emptyList(), ) : StateType { // if you do not delegate - the application crashes on startup, @@ -131,32 +129,6 @@ data class WalletState( return walletsData.find { it.currency == selectedCurrency } } - fun canBeRemoved(walletData: WalletData?): Boolean { - if (walletData == null) return false - - if (!isPrimaryCurrency(walletData)) { - val walletManager = getWalletManager(walletData.currency) - ?: return true - - if (walletData.currency is Currency.Blockchain && - walletManager.cardTokens.isNotEmpty() - ) { - return false - } - - val wallet = walletManager.wallet - - return when (walletData.currency) { - is Currency.Blockchain -> !wallet.hasPendingTransactions() && !wallet.hasSendableAmounts() - is Currency.Token -> { - val token = walletData.currency.token - !wallet.hasPendingTransactions(token) && !wallet.isSendableAmount(token) - } - } - } - return false - } - private fun isPrimaryCurrency(walletData: WalletData): Boolean { return (walletData.currency is Currency.Blockchain && walletData.currency.blockchain == store.state.walletState.primaryBlockchain) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 4030bae09b..d7e6ceba13 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.guard +import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.safeUpdate @@ -11,18 +12,20 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.features.wallet.models.toCurrencies import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.models.WalletDialog +import com.tangem.tap.features.wallet.redux.reducers.toWallet import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.tap.userTokensRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,41 +47,47 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.AddToken -> { - addTokens(listOf(action.token), action.blockchain, walletState, globalState) + addTokens(listOf(action.token), action.blockchain, walletState, globalState, action.save) } is WalletAction.MultiWallet.AddTokens -> { - addTokens(action.tokens, action.blockchain, walletState, globalState) + addTokens(action.tokens, action.blockchain, walletState, globalState, action.save) } is WalletAction.MultiWallet.AddBlockchain -> { action.walletManager?.let { handleAddingWalletManagers(globalState, listOf(action.walletManager)) } + val currencies: List = + (walletState?.currencies ?: emptyList()) + action.blockchain.toCurrencies() - globalState.scanResponse?.let { - currenciesRepository.saveUpdatedCurrency( - cardId = it.card.cardId, - blockchainNetwork = action.blockchain - ) + + if (action.save && globalState.scanResponse != null) { + scope.launch { + userTokensRepository.saveUserTokens( + card = globalState.scanResponse.card, + tokens = currencies, + ) + } } + store.dispatch( WalletAction.LoadFiatRate( coinsList = listOf( Currency.Blockchain( action.blockchain.blockchain, - action.blockchain.derivationPath - ) - ) - ) + action.blockchain.derivationPath, + ), + ), + ), ) store.dispatch( WalletAction.LoadWallet( - action.blockchain, action.walletManager - ) + action.blockchain, action.walletManager, + ), ) } is WalletAction.MultiWallet.SaveCurrencies -> { - val cardId = action.cardId ?: globalState.scanResponse?.card?.cardId ?: return - currenciesRepository.saveCurrencies(cardId, action.blockchainNetworks) + val card = action.card ?: globalState.scanResponse?.card ?: return + scope.launch { userTokensRepository.saveUserTokens(card, action.blockchainNetworks.toCurrencies()) } } is WalletAction.MultiWallet.TryToRemoveWallet -> { val currency = action.currency @@ -89,54 +98,44 @@ class MultiWalletMiddleware { } if (currency.isBlockchain() && walletManager.cardTokens.isNotEmpty()) { - store.dispatchDialogShow(WalletDialog.TokensAreLinkedDialog( - currencyTitle = currency.currencyName, - currencySymbol = currency.currencySymbol - )) + store.dispatchDialogShow( + WalletDialog.TokensAreLinkedDialog( + currencyTitle = currency.currencyName, + currencySymbol = currency.currencySymbol, + ), + ) } else { - store.dispatchDialogShow(WalletDialog.RemoveWalletDialog( - currencyTitle = currency.currencyName, - onOk = { - store.dispatch(WalletAction.MultiWallet.RemoveWallet( - currency = currency, - fromScreen = AppScreen.WalletDetails - )) - store.dispatch(NavigationAction.PopBackTo()) - } - )) + store.dispatchDialogShow( + WalletDialog.RemoveWalletDialog( + currencyTitle = currency.currencyName, + onOk = { + store.dispatch( + WalletAction.MultiWallet.RemoveWallet( + currency = currency, + fromScreen = AppScreen.WalletDetails, + ), + ) + store.dispatch(NavigationAction.PopBackTo()) + }, + ), + ) } } is WalletAction.MultiWallet.RemoveWallet -> { val currency = action.currency - val cardId = globalState.scanResponse?.card?.cardId.guard { - store.dispatchErrorNotification(TapError.UnsupportedState("cardId is NULL")) + val card = globalState.scanResponse?.card.guard { + store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL")) store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) return } - - when (currency) { - is Currency.Blockchain -> { - currenciesRepository.removeBlockchain( - cardId = cardId, - blockchainNetwork = BlockchainNetwork( - blockchain = currency.blockchain, - derivationPath = currency.derivationPath, - tokens = emptyList() - ) - ) - } - is Currency.Token -> { - val walletManager = walletState?.getWalletManager(currency) - if (walletManager != null) { - walletManager.removeToken(currency.token) - currenciesRepository.removeToken( - cardId = cardId, - token = currency.token, - blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager) - ) - } - } + var currencies = walletState?.currencies ?: emptyList() + currencies = currencies.filterNot { it == currency } + if (currency.isBlockchain()) { + currencies + .filter { it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath } } + scope.launch { userTokensRepository.saveUserTokens(card, currencies) } + if (action.fromScreen == AppScreen.AddTokens) { store.dispatch(WalletAction.MultiWallet.SelectWallet(null)) } @@ -148,94 +147,9 @@ class MultiWalletMiddleware { store.dispatch(GlobalAction.Onboarding.Start(it, fromHomeScreen = false)) } } -// is WalletAction.MultiWallet.FindBlockchainsInUse -> { -// val scanResponse = globalState.scanResponse ?: return -// if (scanResponse.supportsHdWallet()) return -// -// val cardFirmware = scanResponse.card.firmwareVersion -// val blockchains = currenciesRepository.getBlockchains(cardFirmware) -// .filterNot { walletState?.blockchains?.contains(it) == true } -// .map { BlockchainNetwork(it, null, emptyList()) } -// val walletManagers = -// tapWalletManager.walletManagerFactory.makeWalletManagersForApp( -// scanResponse, -// blockchains -// ) -// -// scope.launch { -// walletManagers.map { walletManager -> -// async(Dispatchers.IO) { -// walletManager.safeUpdate() -// val wallet = walletManager.wallet -// val coinAmount = wallet.amounts[AmountType.Coin]?.value -// if (coinAmount != null && !coinAmount.isZero()) { -// scope.launch(Dispatchers.Main) { -// val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager) -// if (walletState?.getWalletData(blockchainNetwork) == null) { -// store.dispatch(WalletAction.MultiWallet.AddBlockchain( -// blockchainNetwork, walletManager -// )) -// store.dispatch(WalletAction.LoadWallet.Success( -// wallet = wallet, -// blockchain = blockchainNetwork -// )) -// } -// } -// } -// } -// } -// } -// } -// is WalletAction.MultiWallet.FindTokensInUse -> { -// val scanResponse = globalState.scanResponse ?: return -// if (scanResponse.supportsHdWallet()) return -// -// val walletFactory = tapWalletManager.walletManagerFactory -// val card = scanResponse.card -// -// val walletManager = walletState?.getWalletManager( -// Currency.Blockchain(Blockchain.Ethereum, null) -// ) -// ?: walletFactory.makeWalletManagerForApp( -// scanResponse, -// Currency.Blockchain(Blockchain.Ethereum, null) -// ) -// -// val tokenFinder = walletManager as? TokenFinder ?: return -// scope.launch { -// val result = tokenFinder.findTokens() -// -// withContext(Dispatchers.Main) { -// when (result) { -// is Result.Success -> { -// if (result.data.isNotEmpty()) { -// val blockchainNetwork = BlockchainNetwork( -// walletManager.wallet.blockchain, -// walletManager.wallet.publicKey.derivationPath?.rawPath, -// walletManager.cardTokens.toList() -// ) -// currenciesRepository.saveUpdatedCurrency( -// card.cardId, -// blockchainNetwork -// ) -// store.dispatch( -// WalletAction.MultiWallet.AddBlockchain( -// blockchainNetwork, -// walletManager -// ) -// ) -// store.dispatch( -// WalletAction.MultiWallet.AddTokens( -// walletManager.cardTokens.toList(), -// blockchainNetwork -// ) -// ) -// } -// } -// } -// } -// } -// } + is WalletAction.MultiWallet.ScanToGetDerivations -> { + store.dispatch(WalletAction.Scan) + } } } @@ -249,7 +163,7 @@ class MultiWalletMiddleware { private fun handleAddingWalletManagers( globalState: GlobalState, - walletManagers: List + walletManagers: List, ) { globalState.feedbackManager?.infoHolder?.setWalletsInfo(walletManagers) if (globalState.scanResponse?.isDemoCard() == true) { @@ -259,50 +173,59 @@ class MultiWalletMiddleware { private fun addTokens( tokens: List, blockchainNetwork: BlockchainNetwork, - walletState: WalletState?, globalState: GlobalState? + walletState: WalletState?, globalState: GlobalState?, + save: Boolean, ) { if (tokens.isEmpty()) return val scanResponse = globalState?.scanResponse ?: return val wmFactory = globalState.tapWalletManager.walletManagerFactory + val walletState = walletState ?: return + val walletManager = walletState.getWalletManager(blockchainNetwork)?.also { + if (save) { + val wallets = tokens.mapNotNull { token -> token.toWallet(walletState, blockchainNetwork) } + val currencies = walletState.updateWalletsData(wallets).currencies + scope.launch { userTokensRepository.saveUserTokens(scanResponse.card, currencies) } + } + } ?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also { + store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork.updateTokens(tokens), it, save)) + } - val walletManager = walletState?.getWalletManager(blockchainNetwork) - ?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also { - store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, it)) - } ?: return - - store.dispatch(WalletAction.LoadFiatRate(coinsList = tokens.map { token -> - Currency.Token( - token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath - ) - })) - if (tokens.isNotEmpty()) walletManager.addTokens(tokens) - - currenciesRepository.saveUpdatedCurrency( - cardId = scanResponse.card.cardId, - blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager) + store.dispatch( + WalletAction.LoadFiatRate( + coinsList = tokens.map { token -> + Currency.Token( + token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath, + ) + }, + ), ) + if (tokens.isNotEmpty()) walletManager?.addTokens(tokens) + scope.launch { - when (val result = walletManager.safeUpdate()) { - is com.tangem.common.services.Result.Success -> { - val wallet = result.data - wallet.getTokens() - .filter { tokens.contains(it) } - .mapNotNull { token -> - wallet.getTokenAmount(token)?.let { Pair(token, it) } - } - .forEach { - withContext(Dispatchers.Main) { - store.dispatch( - WalletAction.MultiWallet.TokenLoaded( - it.second, - it.first, - blockchainNetwork - ) - ) + val result = walletManager?.safeUpdate() + withMainContext { + when (result) { + is com.tangem.common.services.Result.Success -> { + val wallet = result.data + wallet.getTokens() + .filter { tokens.contains(it) } + .mapNotNull { token -> + wallet.getTokenAmount(token)?.let { Pair(token, it) } } - } + .forEach { + withContext(Dispatchers.Main) { + store.dispatch( + WalletAction.MultiWallet.TokenLoaded( + it.second, + it.first, + blockchainNetwork, + ), + ) + } + } + } + else -> {} } - is com.tangem.common.services.Result.Failure -> {} } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index ecf9579bb9..256e72fd37 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -108,10 +108,13 @@ class WalletMiddleware { val coinAmount = action.wallet.amounts[AmountType.Coin]?.value if (coinAmount != null && !coinAmount.isZero()) { if (walletState.getWalletData(action.blockchain) == null) { - store.dispatch(WalletAction.MultiWallet.AddBlockchain( - action.blockchain, - null - )) + store.dispatch( + WalletAction.MultiWallet.AddBlockchain( + action.blockchain, + null, + true, + ), + ) store.dispatch(WalletAction.LoadWallet.Success( action.wallet, action.blockchain diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 9821f1f6a7..52ab9c4d75 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -34,11 +34,10 @@ class MultiWalletReducer { val walletManager = action.walletManagers.firstOrNull { it.wallet.blockchain == blockchain.blockchain && (it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath) - } ?: return@mapNotNull null - - val wallet = walletManager.wallet + } + val wallet = walletManager?.wallet val cardToken = if (!state.isMultiwalletAllowed) { - wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) } + wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) } } else { null } @@ -168,9 +167,11 @@ class MultiWalletReducer { state.copy(primaryToken = action.token) is WalletAction.MultiWallet.SaveCurrencies -> state is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy( - showBackupWarning = action.show + showBackupWarning = action.show, ) + is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(missingDerivations = action.blockchains) is WalletAction.MultiWallet.BackupWallet -> state + is WalletAction.MultiWallet.ScanToGetDerivations -> state } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index a4b679512e..79aa56c3df 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -145,18 +145,19 @@ private fun internalReduce(action: Action, state: AppState): WalletState { BalanceStatus.Loading } if (action.blockchain == null) { - val wallets = newState.wallets.map { - it.copy( - walletsData = it.walletsData.map { walletData -> + val wallets = newState.wallets.map { walletStore -> + walletStore.copy( + walletsData = walletStore.walletsData.map { walletData -> walletData.copy( currencyData = walletData.currencyData.copy( - status = balanceStatus, + status = + if (walletStore.walletManager != null) balanceStatus else BalanceStatus.Unreachable, currency = walletData.currencyData.currency, currencySymbol = walletData.currencyData.currencySymbol, ), mainButton = WalletMainButton.SendButton(false), ) - } + }, ) } newState = newState.copy( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 73504ddbfc..9579399e28 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -12,7 +12,7 @@ import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.currenciesRepository +import com.tangem.tap.domain.tokens.CurrenciesRepository import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.redux.ProgressState @@ -76,13 +76,14 @@ class MultiWalletView : WalletView() { handleTotalBalance(binding, state.totalBalance) handleBackupWarning(binding, state.showBackupWarning) + handleRescanWarning(binding, state.missingDerivations.isNotEmpty()) walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken) binding.btnAddToken.setOnClickListener { val card = store.state.globalState.scanResponse!!.card store.dispatch( TokensAction.LoadCurrencies( - supportedBlockchains = currenciesRepository.getBlockchains( + supportedBlockchains = CurrenciesRepository.getBlockchains( card.firmwareVersion, card.isTestCard, ), @@ -111,6 +112,16 @@ class MultiWalletView : WalletView() { } } + private fun handleRescanWarning( + binding: FragmentWalletBinding, + showRescanWarning: Boolean, + ) = with(binding.lWalletRescanWarning) { + root.isVisible = showRescanWarning + root.setOnClickListener { + store.dispatch(WalletAction.MultiWallet.ScanToGetDerivations) + } + } + private fun handleTotalBalance( binding: FragmentWalletBinding, totalBalance: TotalBalance?, diff --git a/app/src/main/res/drawable/ic_scan_card.xml b/app/src/main/res/drawable/ic_scan_card.xml new file mode 100644 index 0000000000..e30ad93bea --- /dev/null +++ b/app/src/main/res/drawable/ic_scan_card.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index d5daad21ea..274f8303fd 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -93,17 +93,6 @@ app:barrierDirection="bottom" app:constraint_referenced_ids="iv_card,tv_twin_card_number" /> - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-de/strings_final.xml b/app/src/main/res/values-de/strings_final.xml index 13aa0a643a..f32a97ae89 100644 --- a/app/src/main/res/values-de/strings_final.xml +++ b/app/src/main/res/values-de/strings_final.xml @@ -86,4 +86,6 @@ Reset to Factory Settings 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. Select network + Scan your card + To access all the networks you need to scan the card \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml index 737bdd59c1..ef550053ca 100644 --- a/app/src/main/res/values-fr/strings_final.xml +++ b/app/src/main/res/values-fr/strings_final.xml @@ -86,4 +86,6 @@ Reset to Factory Settings 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. Select network + Scan your card + To access all the networks you need to scan the card \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml index 737bdd59c1..ef550053ca 100644 --- a/app/src/main/res/values-it/strings_final.xml +++ b/app/src/main/res/values-it/strings_final.xml @@ -86,4 +86,6 @@ Reset to Factory Settings 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. Select network + Scan your card + To access all the networks you need to scan the card \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 09b2609172..b1ffa0ba10 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -102,4 +102,6 @@ Недопустимый Memo. Он не будет добавлен в транзакцию. Недопустимый Tag. Он не будет добавлен в транзакцию. Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. + Отсканируйте карту + Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index adee69f918..4585bee0c1 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -99,4 +99,6 @@ Reset to Factory Settings 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. Select network + Scan your card + To access all the networks you need to scan the card diff --git a/domain/src/main/java/com/tangem/domain/common/extensions/ByteArray.kt b/domain/src/main/java/com/tangem/domain/common/extensions/ByteArray.kt new file mode 100644 index 0000000000..fe59135906 --- /dev/null +++ b/domain/src/main/java/com/tangem/domain/common/extensions/ByteArray.kt @@ -0,0 +1,10 @@ +package com.tangem.domain.common.extensions + +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +fun ByteArray.calculateHmacSha256(key: ByteArray): ByteArray { + val mac: Mac = Mac.getInstance("HmacSHA256") + mac.init(SecretKeySpec(key, "HmacSHA256")) + return mac.doFinal(this) +} \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt index 9a4b2cd8b1..4afd9ed2ef 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt @@ -46,5 +46,27 @@ data class CurrenciesResponse(val currencies: List) { } data class GeoResponse( - val code: String -) : TangemTechResponse \ No newline at end of file + val code: String, +) : TangemTechResponse + +data class UserTokensResponse( + val version: Int = 0, + val group: String = "", + val sort: String = "", + val tokens: List = emptyList(), +) : TangemTechResponse + +data class TokenResponse( + val id: String, + val networkId: String, + val derivationPath: String, + val name: String, + val symbol: String, + val decimals: Int, + val contractAddress: String?, +) : TangemTechResponse + +data class TangemTechError( + val code: Int, + val description: String, +) \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt index 80558732dd..518edd9fec 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt @@ -1,6 +1,9 @@ package com.tangem.network.api.tangemTech +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Path import retrofit2.http.Query /** @@ -29,4 +32,10 @@ interface TangemTechApi { @GET("geo") suspend fun geo(): GeoResponse + + @GET("user-tokens/{user-id}") + suspend fun getUserTokens(@Path(value = "user-id") userId: String): UserTokensResponse + + @PUT("user-tokens/{user-id}") + suspend fun putUserTokens(@Path(value = "user-id") userId: String, @Body userTokens: UserTokensResponse) } \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt index 3bf3bd232e..9f93eddcf9 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt @@ -13,18 +13,16 @@ import kotlinx.coroutines.withContext */ class TangemTechService { private val headerInterceptors = mutableListOf( - CacheControlHttpInterceptor(cacheMaxAge) + CacheControlHttpInterceptor(cacheMaxAge), ) - private var api: TangemTechApi = createApi() - suspend fun coins( contractAddress: String? = null, networkIds: String? = null, active: Boolean? = null, searchText: String? = null, offset: Int? = null, - limit: Int? = null + limit: Int? = null, ): Result = withContext(Dispatchers.IO) { performRequest { api.coins( @@ -33,14 +31,14 @@ class TangemTechService { active = active, searchText = searchText, offset = offset, - limit = limit + limit = limit, ) } } suspend fun rates( currency: String, - ids: List + ids: List, ): Result = withContext(Dispatchers.IO) { performRequest { api.rates(currency.lowercase(), ids.joinToString(",")) @@ -55,6 +53,15 @@ class TangemTechService { performRequest { api.currencies() } } + suspend fun getUserTokens(userId: String): Result = withContext(Dispatchers.IO) { + performRequest { api.getUserTokens(userId) } + } + + suspend fun putUserTokens(userId: String, userTokens: UserTokensResponse): Result = + withContext(Dispatchers.IO) { + performRequest { api.putUserTokens(userId, userTokens) } + } + fun addHeaderInterceptors(interceptors: List) { headerInterceptors.removeAll(interceptors) headerInterceptors.addAll(interceptors) @@ -65,7 +72,7 @@ class TangemTechService { val retrofit = createRetrofitInstance( baseUrl = baseUrl, interceptors = headerInterceptors.toList(), -// logEnabled = true, + logEnabled = true, ) return retrofit.create(TangemTechApi::class.java) } From 1d536906ea166fc064b1db89c12a58497e46ff3a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 21 Sep 2022 13:14:32 +0400 Subject: [PATCH 03/32] Updated on 2026-08-14 --- app/src/main/assets/testnet_tokens.json | 20 ++- .../tap/common/extensions/Blockchain.kt | 4 + .../res/drawable/ic_ethereumfair_no_color.xml | 40 +++++ .../res/drawable/ic_ethereumfair_round.xml | 40 +++++ .../res/drawable/ic_ethereumpow_no_color.xml | 31 ++++ .../res/drawable/ic_ethereumpow_round.xml | 166 ++++++++++++++++++ dependencies.gradle | 2 +- .../domain/common/extensions/Blockchain.kt | 8 + 8 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/ic_ethereumfair_no_color.xml create mode 100644 app/src/main/res/drawable/ic_ethereumfair_round.xml create mode 100644 app/src/main/res/drawable/ic_ethereumpow_no_color.xml create mode 100644 app/src/main/res/drawable/ic_ethereumpow_round.xml diff --git a/app/src/main/assets/testnet_tokens.json b/app/src/main/assets/testnet_tokens.json index 84c055f105..e9e185e5f0 100644 --- a/app/src/main/assets/testnet_tokens.json +++ b/app/src/main/assets/testnet_tokens.json @@ -412,12 +412,22 @@ ] }, { - "id" : "stellar", - "symbol" : "XLM", - "name" : "Stellar", - "networks" : [ + "id": "stellar", + "symbol": "XLM", + "name": "Stellar", + "networks": [ { - "networkId" : "stellar/test" + "networkId": "stellar/test" + } + ] + }, + { + "id": "ethereum-pow-iou", + "symbol": "ETHW", + "name": "Ethereum PoW", + "networks": [ + { + "networkId": "ethereum-pow-iou/test" } ] } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index e2490e7886..3b12f0ec88 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -28,6 +28,8 @@ fun Blockchain.getRoundIconRes(): Int { Blockchain.Dogecoin -> R.drawable.ic_dogecoin_round Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_round Blockchain.Gnosis -> R.drawable.ic_gnosis_round + Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_round + Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_round else -> R.drawable.ic_tangem_logo } } @@ -55,6 +57,8 @@ fun Blockchain.getGreyedOutIconRes(): Int { Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color + Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_no_color + Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_no_color else -> R.drawable.ic_tangem_logo } } diff --git a/app/src/main/res/drawable/ic_ethereumfair_no_color.xml b/app/src/main/res/drawable/ic_ethereumfair_no_color.xml new file mode 100644 index 0000000000..dd68efa83e --- /dev/null +++ b/app/src/main/res/drawable/ic_ethereumfair_no_color.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_ethereumfair_round.xml b/app/src/main/res/drawable/ic_ethereumfair_round.xml new file mode 100644 index 0000000000..b8e6604a7a --- /dev/null +++ b/app/src/main/res/drawable/ic_ethereumfair_round.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_ethereumpow_no_color.xml b/app/src/main/res/drawable/ic_ethereumpow_no_color.xml new file mode 100644 index 0000000000..aa369fd0ab --- /dev/null +++ b/app/src/main/res/drawable/ic_ethereumpow_no_color.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_ethereumpow_round.xml b/app/src/main/res/drawable/ic_ethereumpow_round.xml new file mode 100644 index 0000000000..052ac58398 --- /dev/null +++ b/app/src/main/res/drawable/ic_ethereumpow_round.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dependencies.gradle b/dependencies.gradle index 0fdf41d259..18c09c7c2d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,7 +2,7 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', tangem_card_sdk : 'develop-159', - tangem_blockchain_sdk: 'AND-2102-develop-105-hotfix_xlm-109', + tangem_blockchain_sdk: 'AND-2140_develop_105_hotfix_eth_pow_fair-116', // tangem_blockchain_sdk: '0.0.1', ] diff --git a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 8708f1a0a8..3fd24aa445 100644 --- a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -38,6 +38,9 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "tron/test" -> Blockchain.TronTestnet "xrp", "ripple" -> Blockchain.XRP "xdai" -> Blockchain.Gnosis + "ethereum-pow-iou" -> Blockchain.EthereumPow + "ethereum-pow-iou/test" -> Blockchain.EthereumPowTestnet + "ethereumfair" -> Blockchain.EthereumFair else -> null } } @@ -80,6 +83,9 @@ fun Blockchain.toNetworkId(): String { Blockchain.Tron -> "tron" Blockchain.TronTestnet -> "tron/test" Blockchain.Gnosis -> "xdai" + Blockchain.EthereumPow -> "ethereum-pow-iou" + Blockchain.EthereumPowTestnet -> "ethereum-pow-iou/test" + Blockchain.EthereumFair -> "ethereumfair" } } @@ -105,6 +111,8 @@ fun Blockchain.toCoinId(): String { Blockchain.Dogecoin -> "dogecoin" Blockchain.Tron, Blockchain.TronTestnet -> "tron" Blockchain.Gnosis -> "xdai" + Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> "ethereum-pow-iou" + Blockchain.EthereumFair -> "ethereumfair" Blockchain.Unknown -> "unknown" } } \ No newline at end of file From dfe1e389d334c4a8b7c44bb8a0dd21cb2f75c465 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 22 Sep 2022 12:57:27 +0400 Subject: [PATCH 04/32] Updated on 2026-08-14 --- .../tangem/tap/features/demo/DemoHelper.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt index 650ce5ea09..b0d473cde0 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -448,6 +448,33 @@ class DemoConfig { "AB02000000049533", "AB02000000049541", "AB02000000049830", + // === more cids === + "AC03000000091418", + "AC03000000091400", + "AC03000000099007", + "AC03000000098991", + "AC03000000098942", + "AC03000000091715", + "AC03000000091301", + "AC03000000091343", + "AB01000000055705", + "AB01000000052918", + "AB01000000047710", + "AB01000000052306", + "AB01000000047645", + "AB01000000048957", + "AB01000000052900", + "AB01000000050391", + "AB01000000047363", + "AB02000000053998", + "AB02000000019809", + "AB02000000020872", + "AB02000000022027", + "AB02000000058955", + "AB02000000053253", + "AB02000000048063", + "AB02000000023736", + "AB02000000058187", ) private val testDemoCardIds = listOf( From d02dac09ecb1bba6b01701125b3864c34c36c9f5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 22 Sep 2022 17:07:13 +0400 Subject: [PATCH 05/32] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/TapErrors.kt | 8 +++- .../walletconnect/WalletConnectManager.kt | 33 ++++++++++----- .../walletconnect/extensions/WcPeerMeta.kt | 9 ++++ .../walletconnect/WalletConnectAction.kt | 3 +- .../walletconnect/WalletConnectMiddleware.kt | 41 ++++++++++++------- app/src/main/res/values-de/strings_final.xml | 2 + app/src/main/res/values-fr/strings_final.xml | 2 + app/src/main/res/values-it/strings_final.xml | 2 + app/src/main/res/values-ru/strings_final.xml | 2 + app/src/main/res/values/strings_final.xml | 2 + 10 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/extensions/WcPeerMeta.kt diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 9d3cc79eee..03dc7f645b 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -41,7 +41,7 @@ sealed class TapError( data class UnsupportedState( val stateError: String, - val customMessage: String = "Unsupported state:" + val customMessage: String = "Unsupported state:", ) : TapError(R.string.common_custom_string, listOf("$customMessage $stateError")) sealed class WalletManager { @@ -52,9 +52,13 @@ sealed class TapError( object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) } + sealed class WalletConnect { + object UnsupportedDapp : TapError(R.string.wallet_connect_error_unsupported_dapp) + } + data class ValidateTransactionErrors( override val errorList: List, - override val builder: (List) -> String + override val builder: (List) -> String, ) : TapError(-1), MultiMessageError } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 7e4fae297a..064c3b180b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -5,6 +5,8 @@ import com.tangem.common.extensions.guard import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.walletconnect.extensions.isDappSupported import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession @@ -325,21 +327,30 @@ class WalletConnectManager { val session = client.session val data = sessions[session]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId) if (data != null && session != null) { - sessions[session] = data - val sessionData = data.toWalletConnectSession() - sessionData?.let { + if (!peer.isDappSupported()) { store.dispatchOnMain( - WalletConnectAction.ScanCard( - session = sessionData, - chainId = client.chainId?.toIntOrNull() + WalletConnectAction.FailureEstablishingSession( + session = session, + error = TapError.WalletConnect.UnsupportedDapp, + ), + ) + } else { + sessions[session] = data + val sessionData = data.toWalletConnectSession() + sessionData?.let { + store.dispatchOnMain( + WalletConnectAction.ScanCard( + session = sessionData, + chainId = client.chainId?.toIntOrNull(), + ), ) + } + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Session( + Analytics.WcSessionEvent.Connect, peer.url, + ), ) } - store.state.globalState.analyticsHandlers?.logWcEvent( - Analytics.WcAnalyticsEvent.Session( - Analytics.WcSessionEvent.Connect, peer.url - ) - ) } } client.onSessionUpdate = { id: Long, update: WCSessionUpdate -> diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/extensions/WcPeerMeta.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/extensions/WcPeerMeta.kt new file mode 100644 index 0000000000..057caf7bd2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/extensions/WcPeerMeta.kt @@ -0,0 +1,9 @@ +package com.tangem.tap.domain.walletconnect.extensions + +import com.trustwallet.walletconnect.models.WCPeerMeta + +fun WCPeerMeta.isDappSupported(): Boolean { + return !unsupportedDappsList.any { this.url.contains(it) } +} + +private val unsupportedDappsList: List = listOf("dydx.exchange") \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 14a871fbd9..9106094b1a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.Blockchain import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.domain.TapError import com.tangem.wallet.R import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder @@ -53,7 +54,7 @@ sealed class WalletConnectAction : Action { val updatedSession: WalletConnectSession, ) : WalletConnectAction() - data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction() + data class FailureEstablishingSession(val session: WCSession?, val error: TapError? = null) : WalletConnectAction() data class SetSessionsRestored(val sessions: List) : WalletConnectAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index d98fb0b0f5..faf6594595 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -8,6 +8,7 @@ import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -90,6 +91,16 @@ class WalletConnectMiddleware { store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout)) } is WalletConnectAction.FailureEstablishingSession -> { + if (action.error != null) { + store.dispatch( + GlobalAction.ShowDialog( + AppDialog.SimpleOkDialogRes( + headerId = R.string.common_warning, + messageId = action.error.messageResource, + ), + ), + ) + } if (action.session != null) { walletConnectManager.disconnect(action.session) } @@ -273,21 +284,21 @@ class WalletConnectMiddleware { ) return } - val wallet = walletManager.wallet - val derivedKey = - if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) { - null - } else { - walletManager.wallet.publicKey.blockchainKey - } - val walletForSession = WalletForSession( - cardId = scanResponse.card.cardId, - walletPublicKey = wallet.publicKey.seedKey, - derivedPublicKey = derivedKey, - derivationPath = wallet.publicKey.derivationPath, - derivationStyle = scanResponse.card.derivationStyle, - blockchain = wallet.blockchain, - ) + val wallet = walletManager.wallet + val derivedKey = + if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) { + null + } else { + walletManager.wallet.publicKey.blockchainKey + } + val walletForSession = WalletForSession( + cardId = scanResponse.card.cardId, + walletPublicKey = wallet.publicKey.seedKey, + derivedPublicKey = derivedKey, + derivationPath = wallet.publicKey.derivationPath, + derivationStyle = scanResponse.card.derivationStyle, + blockchain = wallet.blockchain, + ) withMainContext { val updatedSession = session.copy(wallet = walletForSession) diff --git a/app/src/main/res/values-de/strings_final.xml b/app/src/main/res/values-de/strings_final.xml index a2c94fc2da..042969a1b3 100644 --- a/app/src/main/res/values-de/strings_final.xml +++ b/app/src/main/res/values-de/strings_final.xml @@ -99,4 +99,6 @@ Tag Invalid Memo. It won\'t be added to the transaction Invalid Tag. It won\'t be added to the transaction + + Connection with this Dapp cannot be established due to its technical implementation. \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml index 01b15baa13..44cf70e8a4 100644 --- a/app/src/main/res/values-fr/strings_final.xml +++ b/app/src/main/res/values-fr/strings_final.xml @@ -99,4 +99,6 @@ Tag Invalid Memo. It won\'t be added to the transaction Invalid Tag. It won\'t be added to the transaction + + Connection with this Dapp cannot be established due to its technical implementation. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml index f6b98a333f..8052503722 100644 --- a/app/src/main/res/values-it/strings_final.xml +++ b/app/src/main/res/values-it/strings_final.xml @@ -100,4 +100,6 @@ Tag Invalid Memo. It won\'t be added to the transaction Invalid Tag. It won\'t be added to the transaction + + Connection with this Dapp cannot be established due to its technical implementation. \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 3ccd9d13aa..22f9894ae8 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -116,4 +116,6 @@ Tag Недопустимый Memo. Он не будет добавлен в транзакцию. Недопустимый Tag. Он не будет добавлен в транзакцию. + + Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index 0e11c29566..bd35c99d4e 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -118,4 +118,6 @@ Tag Invalid Memo. It won\'t be added to the transaction Invalid Tag. It won\'t be added to the transaction + + Connection with this Dapp cannot be established due to its technical implementation. From f1d61dad13ffd00683a0024a46000d4f783c5041 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 22 Sep 2022 17:09:06 +0400 Subject: [PATCH 06/32] Updated on 2026-08-14 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 18c09c7c2d..e1b2eb8ab6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,7 +2,7 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', tangem_card_sdk : 'develop-159', - tangem_blockchain_sdk: 'AND-2140_develop_105_hotfix_eth_pow_fair-116', + tangem_blockchain_sdk: 'AND-2145_develop_105_hotfix_eth_classic-118', // tangem_blockchain_sdk: '0.0.1', ] From 0e4c0349de82239c6c1e3455f9bda5111d31ffe0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Sep 2022 10:24:28 +0400 Subject: [PATCH 07/32] Updated on 2026-08-14 --- .../wallet/ui/images/CurrencyIconRequest.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt index cd7959319e..f6d1124e89 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt @@ -52,9 +52,6 @@ class CurrencyIconRequest( private fun loadTokenIcon() { loadTokenIconBase( - onStart = { - currencyImageView.setColorFilter(it.getColor()) - }, onSuccess = { currencyImageView.colorFilter = null } @@ -94,17 +91,25 @@ class CurrencyIconRequest( data = getTokenIcon(token, blockchain), placeholderRes = R.drawable.shape_circle, onStart = { - currencyTextView.text = token.symbol.take(1) - currencyTextView.setTextColor(token.getTextColor()) + showPlaceholder(token) onStart(token) }, onSuccess = { currencyTextView.text = null onSuccess(token) }, - onError = { onError(token) }, + onError = { + showPlaceholder(token) + onError(token) + }, ) } + + private fun showPlaceholder(token: Token) { + currencyTextView.text = token.symbol.take(1) + currencyTextView.setTextColor(token.getTextColor()) + currencyImageView.setColorFilter(token.getColor()) + } } private inline fun ImageView.loadIcon( From 5ad45b5e22ef6e66530392da0b46e22440c09104 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Sep 2022 01:00:26 +0400 Subject: [PATCH 08/32] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/Token.kt | 20 +++++---- .../wallet/ui/images/CurrencyIconRequest.kt | 38 +++++++++------- .../wallet/ui/images/CurrencyIconView.kt | 43 ++++++++----------- .../main/res/layout/view_currency_icon.xml | 7 +-- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Token.kt b/app/src/main/java/com/tangem/tap/common/extensions/Token.kt index 10897bc069..59c918d736 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Token.kt @@ -5,19 +5,23 @@ import androidx.annotation.ColorInt import androidx.core.graphics.luminance import androidx.core.graphics.toColorInt import com.tangem.blockchain.common.Token -import com.tangem.wallet.R @ColorInt -fun Token.getColor(): Int { - return try { - ("#" + this.contractAddress.subSequence(2..7).toString()) - .toColorInt() +fun Token.getColor(isTestnet: Boolean = false): Int { + val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4 + + return if (isTestnet) + defaultColor + else try { + ("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt() } catch (exception: Exception) { - R.color.lightGray4 + defaultColor } } @ColorInt -fun Token.getTextColor(): Int { - return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE +fun Token.getTextColor(isTestnet: Boolean = false): Int = when { + isTestnet -> Color.WHITE + this.getColor().luminance > 0.5 -> Color.BLACK + else -> Color.WHITE } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt index f6d1124e89..62f015909c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt @@ -21,7 +21,7 @@ private const val VOYR = "VOYRME" class CurrencyIconRequest( private val currencyImageView: ImageFilterView, - private val currencyTextView: TextView, + private val currencyTextView: TextView?, private val token: Token?, private val blockchain: Blockchain, ) { @@ -38,7 +38,7 @@ class CurrencyIconRequest( loadBlockchainIconBase( onStart = { currencyImageView.colorFilter = null - } + }, ) } @@ -46,15 +46,22 @@ class CurrencyIconRequest( loadBlockchainIconBase( onStart = { currencyImageView.saturation = 0f - } + }, ) } private fun loadTokenIcon() { loadTokenIconBase( + onStart = { + currencyImageView.setColorFilter(it.getColor()) + }, onSuccess = { currencyImageView.colorFilter = null - } + }, + onError = { + currencyImageView.setColorFilter(it.getColor()) + currencyTextView?.setTextColor(it.getTextColor()) + }, ) } @@ -62,7 +69,12 @@ class CurrencyIconRequest( loadTokenIconBase( onStart = { currencyImageView.saturation = 0f - } + }, + onError = { + currencyImageView.saturation = 0f + currencyImageView.setColorFilter(it.getColor(true)) + currencyTextView?.setTextColor(it.getTextColor(true)) + }, ) } @@ -91,25 +103,21 @@ class CurrencyIconRequest( data = getTokenIcon(token, blockchain), placeholderRes = R.drawable.shape_circle, onStart = { - showPlaceholder(token) + currencyTextView?.text = token.name.take(1) + currencyTextView?.setTextColor(token.getTextColor()) onStart(token) }, onSuccess = { - currencyTextView.text = null + currencyTextView?.text = null onSuccess(token) }, onError = { - showPlaceholder(token) + // for some reason the onStart doesn't call if an error occurs + currencyTextView?.text = token.name.take(1) onError(token) }, ) } - - private fun showPlaceholder(token: Token) { - currencyTextView.text = token.symbol.take(1) - currencyTextView.setTextColor(token.getTextColor()) - currencyImageView.setColorFilter(token.getColor()) - } } private inline fun ImageView.loadIcon( @@ -127,7 +135,7 @@ private inline fun ImageView.loadIcon( .listener( onStart = { onStart() }, onSuccess = { _, _ -> onSuccess() }, - onError = { _, _ -> onError() } + onError = { _, _ -> onError() }, ) .target(imageView = this) .build() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt index ec9ff38519..96d03cdd22 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt @@ -4,13 +4,11 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.TextView -import androidx.annotation.DrawableRes import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import com.tangem.blockchain.common.DerivationStyle import com.tangem.tangem_sdk_new.extensions.dpToPx -import com.tangem.tap.common.extensions.getRoundIconRes import com.tangem.tap.features.wallet.models.Currency import com.tangem.wallet.databinding.ViewCurrencyIconBinding import kotlin.math.roundToInt @@ -24,29 +22,16 @@ class CurrencyIconView @JvmOverloads constructor( LayoutInflater.from(context), this, ) - private val currencyImageView: ImageFilterView get() = binding.ivCurrency - private val currencyTextView: TextView get() = binding.tvTokenLetter - - private var isBlockchainIconVisible: Boolean - get() = binding.ivBlockchain.isVisible - set(value) = binding.ivBlockchain::isVisible.set(value) - - private var isBadgeVisible: Boolean - get() = binding.badge.isVisible - set(value) = binding.badge::isVisible.set(value) - - @DrawableRes - private var blockchainIconRes: Int? = null - set(value) { - if (value != null && value != field && isBlockchainIconVisible) { - binding.ivBlockchain.setImageResource(value) - field = value - } - } + private var isBlockchainBadgeVisible: Boolean + get() = binding.ivBlockchainBadge.isVisible + set(value) = binding.ivBlockchainBadge::isVisible.set(value) + private var isCustomCurrencyBadgeVisible: Boolean + get() = binding.customBadge.isVisible + set(value) = binding.customBadge::isVisible.set(value) init { minWidth = dpToPx(48f).roundToInt() @@ -57,9 +42,7 @@ class CurrencyIconView @JvmOverloads constructor( currency: Currency, derivationStyle: DerivationStyle?, ) { - isBlockchainIconVisible = currency.isToken() - isBadgeVisible = currency.isCustomCurrency(derivationStyle) - blockchainIconRes = currency.blockchain.getRoundIconRes() + isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle) CurrencyIconRequest( currencyImageView = currencyImageView, @@ -67,5 +50,17 @@ class CurrencyIconView @JvmOverloads constructor( token = (currency as? Currency.Token)?.token, blockchain = currency.blockchain, ).load() + + if (currency.isToken()) { + // load a blockchain icon into the blockchain badge + isBlockchainBadgeVisible = true + + CurrencyIconRequest( + currencyImageView = binding.ivBlockchainBadge, + currencyTextView = null, + token = null, + blockchain = currency.blockchain, + ).load() + } } } \ No newline at end of file diff --git a/app/src/main/res/layout/view_currency_icon.xml b/app/src/main/res/layout/view_currency_icon.xml index 801ac76c97..aa1330c398 100644 --- a/app/src/main/res/layout/view_currency_icon.xml +++ b/app/src/main/res/layout/view_currency_icon.xml @@ -35,12 +35,13 @@ android:textColor="@android:color/white" android:textSize="25sp" android:textStyle="bold" + android:translationY="-1dp" tools:text="J" /> - Date: Thu, 15 Sep 2022 15:44:08 +0400 Subject: [PATCH 09/32] Updated on 2026-08-14 --- .../wallet/ui/WalletDetailsFragment.kt | 1 + .../wallet/ui/adapters/WalletAdapter.kt | 1 + .../wallet/ui/images/CurrencyIconView.kt | 50 ++++++++++--------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index ab07dd33d5..db9173e486 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -41,6 +41,7 @@ import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter +import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.features.wallet.ui.test.TestWalletDetails import com.tangem.tap.store import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index 4be9104bb1..af061d48f1 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -15,6 +15,7 @@ import com.tangem.tap.common.extensions.show import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletData import com.tangem.tap.features.wallet.ui.BalanceStatus +import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.ItemCurrencyWalletBinding diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt index 96d03cdd22..4f5fd38bf9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt @@ -22,14 +22,16 @@ class CurrencyIconView @JvmOverloads constructor( LayoutInflater.from(context), this, ) - private val currencyImageView: ImageFilterView + val currencyImageView: ImageFilterView get() = binding.ivCurrency - private val currencyTextView: TextView + val currencyTextView: TextView get() = binding.tvTokenLetter - private var isBlockchainBadgeVisible: Boolean + val blockchainBadge: ImageFilterView + get() = binding.ivBlockchainBadge + var isBlockchainBadgeVisible: Boolean get() = binding.ivBlockchainBadge.isVisible set(value) = binding.ivBlockchainBadge::isVisible.set(value) - private var isCustomCurrencyBadgeVisible: Boolean + var isCustomCurrencyBadgeVisible: Boolean get() = binding.customBadge.isVisible set(value) = binding.customBadge::isVisible.set(value) @@ -37,30 +39,30 @@ class CurrencyIconView @JvmOverloads constructor( minWidth = dpToPx(48f).roundToInt() minHeight = dpToPx(48f).roundToInt() } +} - fun load( - currency: Currency, - derivationStyle: DerivationStyle?, - ) { - isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle) +fun CurrencyIconView.load( + currency: Currency, + derivationStyle: DerivationStyle?, +) { + isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle) + + CurrencyIconRequest( + currencyImageView = currencyImageView, + currencyTextView = currencyTextView, + token = (currency as? Currency.Token)?.token, + blockchain = currency.blockchain, + ).load() + + if (currency.isToken()) { + // load a blockchain icon into the blockchain badge + isBlockchainBadgeVisible = true CurrencyIconRequest( - currencyImageView = currencyImageView, - currencyTextView = currencyTextView, - token = (currency as? Currency.Token)?.token, + currencyImageView = blockchainBadge, + currencyTextView = null, + token = null, blockchain = currency.blockchain, ).load() - - if (currency.isToken()) { - // load a blockchain icon into the blockchain badge - isBlockchainBadgeVisible = true - - CurrencyIconRequest( - currencyImageView = binding.ivBlockchainBadge, - currencyTextView = null, - token = null, - blockchain = currency.blockchain, - ).load() - } } } \ No newline at end of file From c2d8710cac28a9d7adf5dec937f61b3e578032c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Sep 2022 20:04:21 +0300 Subject: [PATCH 10/32] Updated on 2026-08-14 --- .../network/exchangeServices/mercuryo/MercuryoService.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index dec1f05bae..35cc636808 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -76,7 +76,12 @@ class MercuryoService( if (!isBuyAllowed()) return false // blockchains which cant be defined by mercuryo service - val unsupportedBlockchains = listOf(Blockchain.Unknown, Blockchain.Binance, Blockchain.Arbitrum) + val unsupportedBlockchains = listOf( + Blockchain.Unknown, + Blockchain.Binance, + Blockchain.Arbitrum, + Blockchain.Optimism, + ) val blockchain = currency.blockchain return when (currency) { From e01b740983d021f0ef96c8d9e9a7697c775fdcbf Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Sep 2022 20:47:06 +0300 Subject: [PATCH 11/32] Updated on 2026-08-14 --- app/src/main/res/values-de/strings_blockchain.xml | 4 ---- app/src/main/res/values-de/strings_final.xml | 4 ++++ app/src/main/res/values-fr/strings_blockchain.xml | 4 ---- app/src/main/res/values-fr/strings_final.xml | 4 ++++ app/src/main/res/values-it/strings_blockchain.xml | 4 ---- app/src/main/res/values-it/strings_final.xml | 4 ++++ app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-ru/strings_final.xml | 5 ++--- app/src/main/res/values/strings_blockchain.xml | 4 ---- app/src/main/res/values/strings_final.xml | 4 ++++ 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-de/strings_blockchain.xml b/app/src/main/res/values-de/strings_blockchain.xml index 5cb1360d71..74a5cca269 100644 --- a/app/src/main/res/values-de/strings_blockchain.xml +++ b/app/src/main/res/values-de/strings_blockchain.xml @@ -18,10 +18,6 @@ Der Gesamtbetrag geht über die Bilanz hinaus Die Gebühr geht über die Bilanz hinaus Minimum balance is %s - Memo - Tag - Invalid Memo. It won\'t be added to the transaction - Invalid Tag. It won\'t be added to the transaction %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-de/strings_final.xml b/app/src/main/res/values-de/strings_final.xml index f32a97ae89..a1d45244e2 100644 --- a/app/src/main/res/values-de/strings_final.xml +++ b/app/src/main/res/values-de/strings_final.xml @@ -88,4 +88,8 @@ Select network Scan your card To access all the networks you need to scan the card + Memo + Tag + Invalid Memo. It won\'t be added to the transaction. + Invalid Tag. It won\'t be added to the transaction. \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_blockchain.xml b/app/src/main/res/values-fr/strings_blockchain.xml index 5bbb85c2f3..c1b985a23a 100644 --- a/app/src/main/res/values-fr/strings_blockchain.xml +++ b/app/src/main/res/values-fr/strings_blockchain.xml @@ -18,10 +18,6 @@ Réduire de %s XTZ Non, envoyer toute la somme Minimum balance is %s - Memo - Tag - Invalid Memo. It won\'t be added to the transaction - Invalid Tag. It won\'t be added to the transaction %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml index ef550053ca..27b10023cd 100644 --- a/app/src/main/res/values-fr/strings_final.xml +++ b/app/src/main/res/values-fr/strings_final.xml @@ -88,4 +88,8 @@ Select network Scan your card To access all the networks you need to scan the card + Memo + Tag + Invalid Memo. It won\'t be added to the transaction. + Invalid Tag. It won\'t be added to the transaction. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_blockchain.xml b/app/src/main/res/values-it/strings_blockchain.xml index 775778dbb0..706ec42b90 100644 --- a/app/src/main/res/values-it/strings_blockchain.xml +++ b/app/src/main/res/values-it/strings_blockchain.xml @@ -18,10 +18,6 @@ Riduci di %s XTZ No, invia l\'intero importo Minimum balance is %s - Memo - Tag - Invalid Memo. It won\'t be added to the transaction - Invalid Tag. It won\'t be added to the transaction %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml index ef550053ca..27b10023cd 100644 --- a/app/src/main/res/values-it/strings_final.xml +++ b/app/src/main/res/values-it/strings_final.xml @@ -88,4 +88,8 @@ Select network Scan your card To access all the networks you need to scan the card + Memo + Tag + Invalid Memo. It won\'t be added to the transaction. + Invalid Tag. It won\'t be added to the transaction. \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ca3e7e93f1..5ab8b7abf9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -359,4 +359,5 @@ Пожалуйста, расскажите нам больше о Вашей проблеме. Каждая деталь может быть полезной. Информация ниже не является обязательной. Вы можете стереть её, если хотите. Нет + Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index b1ffa0ba10..c505594cb4 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -97,11 +97,10 @@ Сброс к заводским настройкам Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать эту карту для восстановления кода доступа. Выберите сеть + Отсканируйте карту + Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту Memo Tag Недопустимый Memo. Он не будет добавлен в транзакцию. Недопустимый Tag. Он не будет добавлен в транзакцию. - Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. - Отсканируйте карту - Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту diff --git a/app/src/main/res/values/strings_blockchain.xml b/app/src/main/res/values/strings_blockchain.xml index 6a496314b1..9291701cb4 100644 --- a/app/src/main/res/values/strings_blockchain.xml +++ b/app/src/main/res/values/strings_blockchain.xml @@ -18,10 +18,6 @@ Reduce by %s XTZ No, send all Minimum balance is %s - Memo - Tag - Invalid Memo. It won\'t be added to the transaction - Invalid Tag. It won\'t be added to the transaction %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index 4585bee0c1..0b5d3d9cd5 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -101,4 +101,8 @@ Select network Scan your card To access all the networks you need to scan the card + Memo + Tag + Invalid Memo. It won\'t be added to the transaction. + Invalid Tag. It won\'t be added to the transaction. From 929c1f08442c73b6f7d0462bd04b126ec1cacfa4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Sep 2022 21:02:22 +0300 Subject: [PATCH 12/32] Updated on 2026-08-14 --- .../com/tangem/tap/features/wallet/models/WalletWarning.kt | 3 +-- .../java/com/tangem/tap/features/wallet/redux/WalletState.kt | 3 +-- .../tangem/tap/features/wallet/ui/WalletWarningConverter.kt | 2 +- app/src/main/res/values-de/strings_blockchain.xml | 2 +- app/src/main/res/values-fr/strings_blockchain.xml | 2 +- app/src/main/res/values-it/strings_blockchain.xml | 2 +- app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-ru/strings_blockchain.xml | 1 + app/src/main/res/values/strings_blockchain.xml | 2 +- 9 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt index eb6c8caf49..3b80a95845 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt @@ -5,8 +5,7 @@ sealed class WalletWarning( ) { data class ExistentialDeposit( val currencyName: String, - val currencySymbols: String, - val existentialDepositString: String, + val edStringValueWithSymbol: String, ) : WalletWarning(1) object TransactionInProgress : WalletWarning(10) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index d6f0def028..75109fec5a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -352,8 +352,7 @@ data class WalletData( if (existentialDepositString != null) { val warning = WalletWarning.ExistentialDeposit( currencyName = currency.currencyName, - currencySymbols = currency.currencySymbol, - existentialDepositString = existentialDepositString, + edStringValueWithSymbol = "$existentialDepositString ${currency.currencySymbol}", ) walletWarnings.add(warning) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt index 6d34bbbf1f..624d34da3e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt @@ -18,7 +18,7 @@ class WalletWarningConverter( is WalletWarning.ExistentialDeposit -> { context.getString( R.string.warning_existential_deposit_message, - message.currencyName, message.currencySymbols, message.existentialDepositString, + message.currencyName, message.edStringValueWithSymbol, ) } is WalletWarning.BalanceNotEnoughForFee -> { diff --git a/app/src/main/res/values-de/strings_blockchain.xml b/app/src/main/res/values-de/strings_blockchain.xml index 74a5cca269..66998f875e 100644 --- a/app/src/main/res/values-de/strings_blockchain.xml +++ b/app/src/main/res/values-de/strings_blockchain.xml @@ -18,6 +18,6 @@ Der Gesamtbetrag geht über die Bilanz hinaus Die Gebühr geht über die Bilanz hinaus Minimum balance is %s - %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_blockchain.xml b/app/src/main/res/values-fr/strings_blockchain.xml index c1b985a23a..197f42551c 100644 --- a/app/src/main/res/values-fr/strings_blockchain.xml +++ b/app/src/main/res/values-fr/strings_blockchain.xml @@ -18,6 +18,6 @@ Réduire de %s XTZ Non, envoyer toute la somme Minimum balance is %s - %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_blockchain.xml b/app/src/main/res/values-it/strings_blockchain.xml index 706ec42b90..92df7b9703 100644 --- a/app/src/main/res/values-it/strings_blockchain.xml +++ b/app/src/main/res/values-it/strings_blockchain.xml @@ -18,6 +18,6 @@ Riduci di %s XTZ No, invia l\'intero importo Minimum balance is %s - %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5ab8b7abf9..ca3e7e93f1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -359,5 +359,4 @@ Пожалуйста, расскажите нам больше о Вашей проблеме. Каждая деталь может быть полезной. Информация ниже не является обязательной. Вы можете стереть её, если хотите. Нет - Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. diff --git a/app/src/main/res/values-ru/strings_blockchain.xml b/app/src/main/res/values-ru/strings_blockchain.xml index 61a4c91467..9ccdfd276e 100644 --- a/app/src/main/res/values-ru/strings_blockchain.xml +++ b/app/src/main/res/values-ru/strings_blockchain.xml @@ -18,5 +18,6 @@ Уменьшить на %s XTZ Нет, отправить все Минимальный баланс: %s + Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. Аккаунт получателя не активирован. Отправьте %s или более для активации аккаунта. \ No newline at end of file diff --git a/app/src/main/res/values/strings_blockchain.xml b/app/src/main/res/values/strings_blockchain.xml index 9291701cb4..8014c2a199 100644 --- a/app/src/main/res/values/strings_blockchain.xml +++ b/app/src/main/res/values/strings_blockchain.xml @@ -18,6 +18,6 @@ Reduce by %s XTZ No, send all Minimum balance is %s - %s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed. + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. Destination account is not active. Send %s or more to activate the account. \ No newline at end of file From 586f0b33e91fe634e2a092361e7f0436948000d3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 Sep 2022 15:44:31 +0400 Subject: [PATCH 13/32] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/tokens/UserTokensRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt index 58c3fb08a4..d05cfc0b8a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -103,7 +103,7 @@ class UserTokensRepository( } companion object { - const val MESSAGE = "AccountID" + const val MESSAGE = "UserWalletID" fun init(context: Context, tangemTechService: TangemTechService): UserTokensRepository { val fileReader = AndroidFileReader(context) val oldUserTokensRepository = OldUserTokensRepository( From bd6223010a7f3d88050d05dc28ff2f44bc815d80 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 Sep 2022 16:03:46 +0400 Subject: [PATCH 14/32] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 65 ++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 4f64bc1d20..845e419e5d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -138,36 +138,7 @@ class TapWalletManager { private suspend fun loadMultiWalletData( scanResponse: ScanResponse, ) { - when (val tokensResult = userTokensRepository.getUserTokens(scanResponse.card)) { - is Result.Success -> { - withMainContext { - val blockchainNetworks = tokensResult.data.toBlockchainNetworks() - val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, tokensResult.data) - store.dispatch( - WalletAction.MultiWallet.AddBlockchains( - blockchains = blockchainNetworks, - walletManagers = walletManagers, - save = false, - ), - ) - - blockchainNetworks.filter { it.tokens.isNotEmpty() } - .map { - store.dispatch( - WalletAction.MultiWallet.AddTokens( - tokens = it.tokens, - blockchain = it, - save = false, - ), - ) - } - checkIfDerivationsAreMissing(blockchainNetworks, scanResponse) - } - } - is Result.Failure -> { - return - } - } + loadUserCurrencies(scanResponse, walletManagerFactory) } private fun checkIfDerivationsAreMissing(blockchainNetworks: List, scanResponse: ScanResponse) { @@ -207,7 +178,41 @@ class TapWalletManager { } } + private suspend fun loadUserCurrencies(scanResponse: ScanResponse, walletManagerFactory: WalletManagerFactory) { + when (val tokensResult = userTokensRepository.getUserTokens(scanResponse.card)) { + is Result.Success -> { + withMainContext { + val blockchainNetworks = tokensResult.data.toBlockchainNetworks() + val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, tokensResult.data) + store.dispatch( + WalletAction.MultiWallet.AddBlockchains( + blockchains = blockchainNetworks, + walletManagers = walletManagers, + save = false, + ), + ) + + blockchainNetworks.filter { it.tokens.isNotEmpty() } + .map { + store.dispatch( + WalletAction.MultiWallet.AddTokens( + tokens = it.tokens, + blockchain = it, + save = false, + ), + ) + } + checkIfDerivationsAreMissing(blockchainNetworks, scanResponse) + } + } + is Result.Failure -> { + return + } + } + } + suspend fun reloadData(data: ScanResponse) { + loadUserCurrencies(data, walletManagerFactory) withContext(Dispatchers.Main) { getActionIfUnknownBlockchainOrEmptyWallet(data)?.let { store.dispatch(it) From ef08a9c454be7fedf293f3380cf9a44d20b15ed7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 Sep 2022 15:18:12 +0300 Subject: [PATCH 15/32] Updated on 2026-08-14 --- .../domain/features/addCustomToken/AddCustomTokenService.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt index 502452e476..a502ae5041 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt @@ -26,7 +26,7 @@ class AddCustomTokenService( result.data.coins.forEach { coin -> val networksWithTheSameAddress = coin.networks .filter { it.contractAddress != null || it.decimalCount != null } - .filter { it.contractAddress == contractAddress } + .filter { it.contractAddress?.equals(contractAddress, ignoreCase = true) == true } .filter { supportedTokenNetworkIds.contains(it.networkId) } if (networksWithTheSameAddress.isNotEmpty()) { val newToken = coin.copy(networks = networksWithTheSameAddress) @@ -45,6 +45,5 @@ class AddCustomTokenService( private fun selectNetworksForSearch(networkId: String?): String { return networkId ?: supportedTokenNetworkIds.joinToString(",") - } } \ No newline at end of file From 0b5f75991f5efdcf323389885bf25db6f0b47051 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 Sep 2022 15:32:40 +0300 Subject: [PATCH 16/32] Updated on 2026-08-14 --- .../tap/features/wallet/ui/wallet/SingleWalletView.kt | 7 ++----- app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values/strings_untranslated.xml | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 9b8a2e759f..a5529a450d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -6,6 +6,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.domain.common.TwinCardNumber import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.fitChipsByGroupWidth +import com.tangem.tap.common.extensions.getQuantityString import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState @@ -85,11 +86,7 @@ class SingleWalletView : WalletView() { private fun setupTwinCards(twinCardsState: TwinCardsState?, binding: FragmentWalletBinding) = with(binding) { twinCardsState?.cardNumber?.let { cardNumber -> tvTwinCardNumber.show() - val number = when (cardNumber) { - TwinCardNumber.First -> 1 - TwinCardNumber.Second -> 2 - } - tvTwinCardNumber.text = fragment?.getString(R.string.wallet_twins_chip_format, number, 2) + tvTwinCardNumber.text = tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, 2) } if (twinCardsState?.cardNumber == null) { tvTwinCardNumber.hide() diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ca3e7e93f1..38ee22a3ca 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -129,7 +129,6 @@ Tangem Twin Один кошелек. Две карты. Одна, которую Вы держите в руках, и вторая карта с номером #%s - Карта %1d из %2d Пересоздать Twin кошелек Условия использования карты Tangem Twin diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index afdc44412f..8d6c5017af 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -16,8 +16,6 @@ One wallet. Two cards. This one that you are holding in your hands and the other one with number #%s. - Card %1d of %2d - Re-create twin wallet Card terms of use From 459e4dcd681fbd7621ee017b316d0e8b0beece06 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 Sep 2022 23:11:15 +0300 Subject: [PATCH 17/32] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/common/extensions/Blockchain.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index 71165c14df..634f3ab125 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -29,6 +29,7 @@ fun Blockchain.getRoundIconRes(): Int { Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_round Blockchain.Gnosis -> R.drawable.ic_gnosis_round Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_round + Blockchain.Kusama -> R.drawable.ic_kusama_round Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism Blockchain.Dash -> R.drawable.ic_dash else -> R.drawable.ic_tangem_logo @@ -59,6 +60,7 @@ fun Blockchain.getGreyedOutIconRes(): Int { Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_no_color + Blockchain.Kusama -> R.drawable.ic_kusama_no_color Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism_no_color Blockchain.Dash -> R.drawable.ic_dash_no_color else -> R.drawable.ic_tangem_logo From 5e7d0d3d0f2b326648dda043ac21757b7ab6789b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 10:18:55 +0400 Subject: [PATCH 18/32] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 42 ++++++++----------- .../tap/domain/tokens/UserTokensRepository.kt | 14 +++---- .../tap/features/wallet/redux/WalletAction.kt | 6 ++- .../tap/features/wallet/redux/WalletState.kt | 1 + .../wallet/redux/reducers/WalletReducer.kt | 2 + .../wallet/ui/wallet/MultiWalletView.kt | 2 + .../ui/wallet/SaltPaySingleWalletView.kt | 1 + .../wallet/ui/wallet/SingleWalletView.kt | 1 + app/src/main/res/layout/fragment_wallet.xml | 22 +++++++++- 9 files changed, 58 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 845e419e5d..2ea48fb328 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -179,35 +179,29 @@ class TapWalletManager { } private suspend fun loadUserCurrencies(scanResponse: ScanResponse, walletManagerFactory: WalletManagerFactory) { - when (val tokensResult = userTokensRepository.getUserTokens(scanResponse.card)) { - is Result.Success -> { - withMainContext { - val blockchainNetworks = tokensResult.data.toBlockchainNetworks() - val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, tokensResult.data) + val userTokens = userTokensRepository.getUserTokens(scanResponse.card) + withMainContext { + val blockchainNetworks = userTokens.toBlockchainNetworks() + val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, userTokens) + store.dispatch( + WalletAction.MultiWallet.AddBlockchains( + blockchains = blockchainNetworks, + walletManagers = walletManagers, + save = false, + ), + ) + + blockchainNetworks.filter { it.tokens.isNotEmpty() } + .map { store.dispatch( - WalletAction.MultiWallet.AddBlockchains( - blockchains = blockchainNetworks, - walletManagers = walletManagers, + WalletAction.MultiWallet.AddTokens( + tokens = it.tokens, + blockchain = it, save = false, ), ) - - blockchainNetworks.filter { it.tokens.isNotEmpty() } - .map { - store.dispatch( - WalletAction.MultiWallet.AddTokens( - tokens = it.tokens, - blockchain = it, - save = false, - ), - ) - } - checkIfDerivationsAreMissing(blockchainNetworks, scanResponse) } - } - is Result.Failure -> { - return - } + checkIfDerivationsAreMissing(blockchainNetworks, scanResponse) } } diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt index d05cfc0b8a..e418215ed7 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -24,20 +24,20 @@ class UserTokensRepository( private val storageService: UserTokensStorageService, private val networkService: UserTokensNetworkService, ) { - suspend fun getUserTokens(card: Card): Result> { + suspend fun getUserTokens(card: Card): List { if (DemoHelper.isDemoCardId(card.cardId)) { - return Result.Success(loadDemoCurrencies()) + return loadDemoCurrencies() } val userId = card.getUserId() if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - return Result.Success(loadTokensOffline(card, userId)) + return loadTokensOffline(card, userId) } return when (val networkResult = networkService.getUserTokens(userId)) { is Result.Success -> { val tokens = networkResult.data.tokens.map { Currency.fromTokenResponse(it) } storageService.saveUserTokens(card.getUserId(), tokens) - Result.Success(tokens) + tokens } is Result.Failure -> { handleGetUserTokensFailure(card = card, userId = userId, error = networkResult.error) @@ -73,16 +73,16 @@ class UserTokensRepository( card: Card, userId: String, error: Throwable, - ): Result> { + ): List { return when (error) { is NoDataError -> { val tokens = storageService.getUserTokens(card) coroutineScope { launch { networkService.saveUserTokens(userId = userId, tokens = tokens) } } - Result.Success(tokens) + tokens } else -> { val tokens = storageService.getUserTokens(userId) ?: storageService.getUserTokens(card) - Result.Success(tokens) + tokens } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index ed617762f4..836d287739 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -39,7 +39,7 @@ sealed class WalletAction : Action { data class NoAccount( val wallet: Wallet, val blockchain: BlockchainNetwork, - val amountToCreateAccount: String + val amountToCreateAccount: String, ) : WalletAction() data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction() @@ -47,6 +47,10 @@ sealed class WalletAction : Action { data class SetArtworkId(val artworkId: String?) : WalletAction() + sealed class UserTokens : WalletAction() { + object Loading : UserTokens() + object Loaded : UserTokens() + } sealed class MultiWallet : WalletAction() { data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index d6f0def028..f81d4662a5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -45,6 +45,7 @@ data class WalletState( val totalBalance: TotalBalance? = null, val showBackupWarning: Boolean = false, val missingDerivations: List = emptyList(), + val loadingUserTokens: Boolean = false, ) : StateType { // if you do not delegate - the application crashes on startup, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 79aa56c3df..fbac7c7daa 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -314,6 +314,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.AppCurrencyAction -> { newState = appCurrencyReducer.reduce(action, newState) } + is WalletAction.UserTokens.Loading -> newState = newState.copy(loadingUserTokens = true) + is WalletAction.UserTokens.Loaded -> newState = newState.copy(loadingUserTokens = false) else -> { /* no-op */ } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 9015ba4fc4..9d4fe68894 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -81,6 +81,8 @@ class MultiWalletView : WalletView() { handleRescanWarning(binding, state.missingDerivations.isNotEmpty()) walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken) + binding.pbLoadingUserTokens.show(state.loadingUserTokens) + binding.btnAddToken.setOnClickListener { val card = store.state.globalState.scanResponse!!.card store.dispatch( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt index 09d00b294e..0139a7493c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt @@ -20,6 +20,7 @@ class SaltPaySingleWalletView : WalletView() { btnAddToken.hide() rvPendingTransaction.hide() tvTwinCardNumber.hide() + pbLoadingUserTokens.hide() lCardBalance.root.hide() lAddress.root.hide() lSingleWalletBalance.root.show() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 9b8a2e759f..ef8d0b746b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -36,6 +36,7 @@ class SingleWalletView : WalletView() { rvMultiwallet.hide() btnAddToken.hide() rvPendingTransaction.hide() + pbLoadingUserTokens.hide() lCardBalance.root.show() lAddress.root.show() lSingleWalletBalance.root.hide() diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index 274f8303fd..60b1a2b33e 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -191,11 +191,31 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" + android:layout_marginBottom="16dp" android:nestedScrollingEnabled="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" /> + + + + From aa88f7a4160a7668105addc89fd92dbe0501f1d3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 10:23:42 +0400 Subject: [PATCH 19/32] Updated on 2026-08-14 --- .../main/res/drawable/ic_tangem_warning.xml | 24 +++++++++++++++++++ .../layout/layout_wallet_rescan_warning.xml | 14 +++++------ 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/drawable/ic_tangem_warning.xml diff --git a/app/src/main/res/drawable/ic_tangem_warning.xml b/app/src/main/res/drawable/ic_tangem_warning.xml new file mode 100644 index 0000000000..aa1e0c73ee --- /dev/null +++ b/app/src/main/res/drawable/ic_tangem_warning.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/app/src/main/res/layout/layout_wallet_rescan_warning.xml b/app/src/main/res/layout/layout_wallet_rescan_warning.xml index 8d8e2f1233..c795c2c22a 100644 --- a/app/src/main/res/layout/layout_wallet_rescan_warning.xml +++ b/app/src/main/res/layout/layout_wallet_rescan_warning.xml @@ -8,9 +8,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="16dp" - android:paddingTop="14dp" + android:paddingTop="12dp" android:paddingEnd="16dp" - android:paddingBottom="14dp"> + android:paddingBottom="12dp"> @@ -33,8 +33,8 @@ android:layout_marginStart="8dp" android:layout_marginEnd="16dp" android:text="@string/main_scan_card_warning_view_title" - android:textColor="@color/textBlack" - android:textSize="14sp" + android:textColor="@color/text_primary_1" + android:textSize="16sp" android:textStyle="bold" app:layout_constraintBottom_toTopOf="@id/tv_rescan_warning_subtitle" app:layout_constraintEnd_toStartOf="@id/iv_rescan_warning_end_icon" @@ -47,8 +47,8 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:text="@string/main_scan_card_warning_view_subtitle" - android:textColor="@color/iconGray" - android:textSize="12sp" + android:textColor="@color/text_secondary" + android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/tv_rescan_warning_title" app:layout_constraintStart_toStartOf="@id/tv_rescan_warning_title" From 1f9a74d40fa4db691e8e13018ec6a88d9c16d54b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 10:49:13 +0400 Subject: [PATCH 20/32] Updated on 2026-08-14 --- .../redux/OnboardingOtherCardsMiddleware.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index e470680dbc..02950c3b3a 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -79,17 +79,21 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { val primaryBlockchain = updatedResponse.getBlockchain() val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) { val primaryToken = updatedResponse.getPrimaryToken() - val blockchainNetwork = BlockchainNetwork(primaryBlockchain, updatedResponse.card).updateTokens( - listOfNotNull(primaryToken)) + val blockchainNetwork = + BlockchainNetwork(primaryBlockchain, updatedResponse.card).updateTokens( + listOfNotNull(primaryToken), + ) listOf(blockchainNetwork) } else { listOf( BlockchainNetwork(Blockchain.Bitcoin, updatedResponse.card), - BlockchainNetwork(Blockchain.Ethereum, updatedResponse.card) + BlockchainNetwork(Blockchain.Ethereum, updatedResponse.card), ) } - store.dispatch(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks)) + store.dispatch( + WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks, updatedResponse.card), + ) delay(DELAY_SDK_DIALOG_CLOSE) store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) From de07248bbaa29db5962b340d69d0d057ae1d7c23 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 13:57:23 +0400 Subject: [PATCH 21/32] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 13 ------------- app/src/main/res/values-v29/zendesk_styles.xml | 3 +-- app/src/main/res/values/zendesk_styles.xml | 5 ++++- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b460cf581a..4422e037f9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ android:name="android.hardware.nfc" android:required="true" /> - @@ -131,18 +130,6 @@ - - - - - - - - - - diff --git a/app/src/main/res/values/zendesk_styles.xml b/app/src/main/res/values/zendesk_styles.xml index ebb4fcab6b..2513114d19 100644 --- a/app/src/main/res/values/zendesk_styles.xml +++ b/app/src/main/res/values/zendesk_styles.xml @@ -1,6 +1,9 @@ - From d0fe9b8b099c2b1684ff72b3e50d9482af216e56 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 16:58:03 +0400 Subject: [PATCH 22/32] Updated on 2026-08-14 --- .../tangem/tap/features/details/ui/details/DetailsScreen.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt index 24129ec514..e0b4f1f186 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -86,7 +87,7 @@ fun WalletConnectDetailsItem( ) { Row( modifier = modifier - .height(84.dp) + .defaultMinSize(minHeight = 84.dp) .fillMaxWidth() .clickable { onItemsClick(SettingsElement.WalletConnect) }, horizontalArrangement = Arrangement.Start, @@ -99,7 +100,7 @@ fun WalletConnectDetailsItem( tint = colorResource(id = R.color.all_colors_azure), ) Column( - modifier = modifier.height(56.dp), + modifier = modifier.defaultMinSize(minHeight = 56.dp), horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.Center, ) { From 90e5b0012ba2d6680dcc82bbbaf0859feadf4558 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 17:21:29 +0400 Subject: [PATCH 23/32] Updated on 2026-08-14 --- .../details/ui/walletconnect/WalletConnectScreen.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt index 8474878afc..168bd4a024 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt @@ -140,13 +140,12 @@ private fun WalletConnectSessions( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - Column(modifier = modifier) { - Text( - text = session.description, - style = TangemTypography.subtitle1, - color = colorResource(id = R.color.text_primary_1), - ) - } + Text( + text = session.description, + style = TangemTypography.subtitle1, + color = colorResource(id = R.color.text_primary_1), + modifier = modifier.weight(1f), + ) IconButton( onClick = { state.onRemoveSession(session.sessionId) }, ) { From 937011724fcc8242c3f90b8cbb874b22883cbcd4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 17:30:00 +0400 Subject: [PATCH 24/32] Updated on 2026-08-14 --- .../domain/walletconnect/WalletConnectManager.kt | 14 ++++++++++++-- .../redux/walletconnect/WalletConnectAction.kt | 8 ++++++-- .../redux/walletconnect/WalletConnectMiddleware.kt | 12 ++++++++++++ app/src/main/res/values-de/strings_final.xml | 1 + app/src/main/res/values-fr/strings_final.xml | 1 + app/src/main/res/values-it/strings_final.xml | 1 + app/src/main/res/values-ru/strings_final.xml | 1 + app/src/main/res/values/strings_final.xml | 1 + 8 files changed, 35 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 45c3dee2e3..8a0362acb0 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -57,7 +57,10 @@ class WalletConnectManager { private var sessions: MutableMap = mutableMapOf() fun connect(wcUri: String) { - val session = WCSession.from(wcUri) ?: return + val session = WCSession.from(wcUri).guard { + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null, true)) + return + } if (sessions[session] != null) { store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession) return @@ -65,7 +68,14 @@ class WalletConnectManager { val client = WCClient(httpClient = okHttpClient) setListeners(client) val peerId = UUID.randomUUID().toString() - client.connect(session, tangemPeerMeta, peerId) + + try { + client.connect(session, tangemPeerMeta, peerId) + } catch (exception: IllegalArgumentException) { + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null, true)) + return + } + sessions[session] = WalletConnectActiveData( peerId = peerId, remotePeerId = null, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 0258af7316..5947f349ef 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -52,7 +52,11 @@ sealed class WalletConnectAction : Action { val updatedSession: WalletConnectSession, ) : WalletConnectAction() - data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction() + data class FailureEstablishingSession( + val session: WCSession?, + val showWarning: Boolean = false, + ) : WalletConnectAction() + data class SetSessionsRestored(val sessions: List) : WalletConnectAction() @@ -103,6 +107,6 @@ sealed class WalletConnectAction : Action { data class Sign( val id: Long, val data: ByteArray, val sessionData: WCSession, - ) : WalletConnectAction() + ) : WalletConnectAction() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index cad8f8ab14..9265253b3a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -22,6 +23,7 @@ import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.wallet.R import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -99,6 +101,16 @@ class WalletConnectMiddleware { if (action.session != null) { walletConnectManager.disconnect(action.session) } + if (action.showWarning) { + store.dispatchOnMain( + GlobalAction.ShowDialog( + AppDialog.SimpleOkDialogRes( + headerId = R.string.common_warning, + messageId = R.string.wallet_connect_error_failed_to_connect, + ), + ), + ) + } } is WalletConnectAction.UnsupportedCard -> { store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedCard)) diff --git a/app/src/main/res/values-de/strings_final.xml b/app/src/main/res/values-de/strings_final.xml index f32a97ae89..08c0072255 100644 --- a/app/src/main/res/values-de/strings_final.xml +++ b/app/src/main/res/values-de/strings_final.xml @@ -88,4 +88,5 @@ Select network Scan your card To access all the networks you need to scan the card + Failed to establish WalletConnect session \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml index ef550053ca..16105d506d 100644 --- a/app/src/main/res/values-fr/strings_final.xml +++ b/app/src/main/res/values-fr/strings_final.xml @@ -88,4 +88,5 @@ Select network Scan your card To access all the networks you need to scan the card + Failed to establish WalletConnect session \ No newline at end of file diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml index ef550053ca..16105d506d 100644 --- a/app/src/main/res/values-it/strings_final.xml +++ b/app/src/main/res/values-it/strings_final.xml @@ -88,4 +88,5 @@ Select network Scan your card To access all the networks you need to scan the card + Failed to establish WalletConnect session \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index b1ffa0ba10..225d7eb8cb 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -104,4 +104,5 @@ Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. Отсканируйте карту Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту + Не удалось установить сессию WalletConnect diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index 4585bee0c1..bfa90d2130 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -101,4 +101,5 @@ Select network Scan your card To access all the networks you need to scan the card + Failed to establish WalletConnect session From d9f57b33b1fafd875c9d8ace2da413247ef8ee78 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 17:41:10 +0400 Subject: [PATCH 25/32] Updated on 2026-08-14 --- .../tap/features/wallet/redux/WalletState.kt | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index f81d4662a5..933ca823bc 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -193,21 +193,28 @@ data class WalletState( fun removeWallet(walletData: WalletData?): WalletState { if (walletData == null) return this - return if (walletData.currency is Currency.Blockchain) { - val walletStores = wallets.filterNot { - it.blockchainNetwork.blockchain == walletData.currency.blockchain - && it.blockchainNetwork.derivationPath == walletData.currency.derivationPath + return when (val currency = walletData.currency) { + is Currency.Blockchain -> { + val walletStores = wallets.filterNot { + it.blockchainNetwork.blockchain == currency.blockchain + && it.blockchainNetwork.derivationPath == currency.derivationPath + } + copy(wallets = walletStores) + .updateTotalBalance() + .updateProgressState() + } + is Currency.Token -> { + val walletStore = getWalletStore(walletData.currency) + val walletDataList = walletStore?.walletsData + ?.filterNot { it.currency == walletData.currency } + ?: emptyList() + val updatedWalletManager = walletStore?.walletManager?.also { it.removeToken(currency.token) } + val updatedWalletStore = walletStore?.copy( + walletsData = walletDataList, + walletManager = updatedWalletManager, + ) + updateWalletStore(updatedWalletStore) } - copy(wallets = walletStores) - .updateTotalBalance() - .updateProgressState() - } else { - val walletStore = getWalletStore(walletData.currency) - val walletDataList = walletStore?.walletsData - ?.filterNot { it.currency == walletData.currency } - ?: emptyList() - val updatedWalletStore = walletStore?.copy(walletsData = walletDataList) - updateWalletStore(updatedWalletStore) } } From 5db29f6fea8458c9a8bac2fca9c31bfdd2915571 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 17:49:45 +0400 Subject: [PATCH 26/32] Updated on 2026-08-14 --- .../tap/features/wallet/ui/adapters/WalletAdapter.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index af061d48f1..a688bb9e42 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -110,8 +110,12 @@ class WalletAdapter lContent.tvExchangeRate.text = wallet.fiatRateString ?: root.getString(id = R.string.token_item_no_rate) - cardWallet.setOnClickListener { - store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet)) + if (wallet.walletAddresses != null) { + cardWallet.setOnClickListener { + store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet)) + } + } else { + cardWallet.setOnClickListener(null) } } } From 9f14ffc2e0b128b5edcc75aba8a3f9b6de3f0f9a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 29 Sep 2022 22:33:01 +0500 Subject: [PATCH 27/32] Updated on 2026-08-14 --- .../common/feedback/AdditionalFeedbackInfo.kt | 84 ++++++++++--------- .../common/feedback/FeedbackDataBuilder.kt | 22 +++-- .../send/redux/middlewares/SendMiddleware.kt | 3 +- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt index 70912f24a6..97c8a9e41f 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt @@ -7,16 +7,19 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.address.Address +import com.tangem.common.card.CardWallet import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.extensions.stripZeroPlainString class AdditionalFeedbackInfo { class EmailWalletInfo( var blockchain: Blockchain = Blockchain.Unknown, - var address: String = "", - var explorerLink: String = "", - var host: String = "", var derivationPath: String = "", + var outputsCount: String? = null, + var host: String = "", + var addresses: String = "", + var explorerLink: String = "", ) var appVersion: String = "" @@ -48,23 +51,14 @@ class AdditionalFeedbackInfo { cardBlockchain = data.walletData?.blockchain ?: "" cardFirmwareVersion = data.card.firmwareVersion.stringValue cardIssuer = data.card.issuer.name - signedHashesCount = data.card.wallets - .joinToString("; ") { "${it.curve.curve} - ${it.totalSignedHashes}" } + signedHashesCount = formatSignedHashes(data.card.wallets) } fun setWalletsInfo(walletManagers: List) { walletsInfo.clear() tokens.clear() walletManagers.forEach { manager -> - walletsInfo.add( - EmailWalletInfo( - blockchain = manager.wallet.blockchain, - address = getAddress(manager.wallet), - explorerLink = getExploreUri(manager.wallet), - host = manager.currentHost, - derivationPath = manager.wallet.publicKey.derivationPath?.rawPath ?: "" - ) - ) + walletsInfo.add(createEmailWalletInfo(manager)) if (manager.cardTokens.isNotEmpty()) { tokens[manager.wallet.blockchain] = manager.cardTokens } @@ -72,45 +66,55 @@ class AdditionalFeedbackInfo { } fun updateOnSendError( - wallet: Wallet, - host: String, + walletManager: WalletManager, amountToSend: Amount, feeAmount: Amount, destinationAddress: String, ) { - onSendErrorWalletInfo = EmailWalletInfo( - blockchain = wallet.blockchain, - address = getAddress(wallet), - explorerLink = getExploreUri(wallet), - host = host, - derivationPath = wallet.publicKey.derivationPath?.rawPath ?: "" - ) - + onSendErrorWalletInfo = createEmailWalletInfo(walletManager) this.destinationAddress = destinationAddress amount = amountToSend.value?.stripZeroPlainString() ?: "0" fee = feeAmount.value?.stripZeroPlainString() ?: "0" token = if (amountToSend.type is AmountType.Token) amountToSend.currencySymbol else "" } - private fun getAddress(wallet: Wallet): String { - return if (wallet.addresses.size == 1) { - wallet.address - } else { - val addresses = wallet.addresses.joinToString(", ") { - "${it.type.javaClass.simpleName} - ${it.value}" - } - "Multiple address: $addresses" + private fun createEmailWalletInfo(walletManager: WalletManager): EmailWalletInfo { + return EmailWalletInfo( + blockchain = walletManager.wallet.blockchain, + derivationPath = walletManager.wallet.publicKey.derivationPath?.rawPath ?: "", + outputsCount = walletManager.outputsCount?.toString(), + host = walletManager.currentHost, + addresses = formatAddresses(walletManager.wallet), + explorerLink = formatExploreUrls(walletManager.wallet), + ) + } + + private fun formatSignedHashes(wallets: List): String { + return wallets.joinToString("\n") { "Signed hashes: ${it.curve.curve} - ${it.totalSignedHashes}" } + } + + private fun formatAddresses(wallet: Wallet): String { + return wallet.formatAddressWith("Multiple address:") { + "${it.name} - ${it.value}" } } - private fun getExploreUri(wallet: Wallet): String { - return if (wallet.addresses.size == 1) { - wallet.getExploreUrl(wallet.address) - } else { - val links = wallet.addresses.joinToString(", ") { - "${it.type.javaClass.simpleName} - ${wallet.getExploreUrl(it.value)}" - } - "Multiple explorers links: $links" + private fun formatExploreUrls(wallet: Wallet): String { + return wallet.formatAddressWith("Multiple explorers links:") { + "${it.name} - ${wallet.getExploreUrl(it.value)}" } } + + private fun Wallet.formatAddressWith(with: String, mapAddress: (Address) -> String): String { + return if (addresses.size == 1) { + getExploreUrl(address) + } else { + addresses.map { mapAddress(it) }.toMutableList() + .apply { add(0, with) } + .joinToString("\n") + } + } + + private val Address.name: String + get() = type.javaClass.simpleName } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt index a3a9ee5575..46cf302fee 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt @@ -3,7 +3,7 @@ package com.tangem.tap.common.feedback import com.tangem.tap.common.extensions.breakLine class FeedbackDataBuilder( - private val infoHolder: AdditionalFeedbackInfo + private val infoHolder: AdditionalFeedbackInfo, ) { val builder = StringBuilder() @@ -21,7 +21,7 @@ class FeedbackDataBuilder( builder.appendKeyValue("Card ID", infoHolder.cardId) builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) builder.appendKeyValue("Card Blockchain", infoHolder.cardBlockchain) - builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) + builder.appendKeyValue("", infoHolder.signedHashesCount) return this } @@ -29,20 +29,22 @@ class FeedbackDataBuilder( infoHolder.walletsInfo.forEach { builder.appendDelimiter() builder.appendKeyValue("Blockchain", it.blockchain.fullName) - builder.appendKeyValue("Host", it.host) - builder.appendKeyValue("Wallet address", it.address) builder.appendKeyValue("Derivation path", it.derivationPath) - builder.appendKeyValue("Explorer link", it.explorerLink) + builder.appendKeyValue("Outputs count", it.outputsCount) infoHolder.tokens[it.blockchain]?.let { tokens -> builder.append("Tokens:") breakLine() tokens.forEach { token -> - builder.appendKeyValue("Name", token.name) builder.appendKeyValue("ID", token.id ?: "[custom token]") + builder.appendKeyValue("Name", token.name) builder.appendKeyValue("Contract address", token.contractAddress) } } + + builder.appendKeyValue("Host", it.host) + builder.appendKeyValue("Wallet address", it.addresses) + builder.appendKeyValue("Explorer link", it.explorerLink) } return this } @@ -55,7 +57,7 @@ class FeedbackDataBuilder( builder.appendKeyValue("Token", infoHolder.token) builder.appendKeyValue("Error", error) builder.appendDelimiter() - builder.appendKeyValue("Source address", walletInfo.address) + builder.appendKeyValue("Source address", walletInfo.addresses) builder.appendKeyValue("Destination address", infoHolder.destinationAddress) builder.appendKeyValue("Amount", infoHolder.amount) builder.appendKeyValue("Fee", infoHolder.fee) @@ -72,8 +74,10 @@ class FeedbackDataBuilder( fun build(): String = builder.toString() } -private fun StringBuilder.appendKeyValue(key: String, value: String): StringBuilder { - return if (value.isNotBlank()) this.append("$key: $value\n") else this +private fun StringBuilder.appendKeyValue(key: String, value: String?): StringBuilder = when { + value.isNullOrBlank() -> this + key.isBlank() -> this.append("$value\n") + else -> this.append("$key: $value\n") } private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n") \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 54be369642..defca803e3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -246,8 +246,7 @@ private fun sendTransaction( } is SimpleResult.Failure -> { store.state.globalState.feedbackManager?.infoHolder?.updateOnSendError( - wallet = walletManager.wallet, - host = walletManager.currentHost, + walletManager = walletManager, amountToSend = amountToSend, feeAmount = feeAmount, destinationAddress = destinationAddress, From c307bc2110ff198d88c2069479d5e6740160938c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Sep 2022 12:21:42 +0400 Subject: [PATCH 28/32] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/TapErrors.kt | 1 + .../domain/walletconnect/WalletConnectManager.kt | 14 ++++++++++++-- app/src/main/res/values-ru/strings_final.xml | 1 - dependencies.gradle | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 0b74cb1912..5a0cf85668 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -55,6 +55,7 @@ sealed class TapError( sealed class WalletConnect { object UnsupportedDapp : TapError(R.string.wallet_connect_error_unsupported_dapp) + object UnsupportedLink : TapError(R.string.wallet_connect_error_failed_to_connect) } data class ValidateTransactionErrors( diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 0a108530df..de71029d2c 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -60,7 +60,12 @@ class WalletConnectManager { fun connect(wcUri: String) { val session = WCSession.from(wcUri).guard { - store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null, true)) + store.dispatchOnMain( + WalletConnectAction.FailureEstablishingSession( + session = null, + error = TapError.WalletConnect.UnsupportedLink, + ), + ) return } if (sessions[session] != null) { @@ -74,7 +79,12 @@ class WalletConnectManager { try { client.connect(session, tangemPeerMeta, peerId) } catch (exception: IllegalArgumentException) { - store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null, true)) + store.dispatchOnMain( + WalletConnectAction.FailureEstablishingSession( + session = null, + error = TapError.WalletConnect.UnsupportedLink, + ), + ) return } diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 7e9e83795e..81edc55d19 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -103,7 +103,6 @@ Tag Недопустимый Memo. Он не будет добавлен в транзакцию. Недопустимый Tag. Он не будет добавлен в транзакцию. - Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. Не удалось установить сессию WalletConnect Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. diff --git a/dependencies.gradle b/dependencies.gradle index 5d1d0d6fa5..0b11f515b1 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,7 +2,7 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', tangem_card_sdk : 'develop-159', - tangem_blockchain_sdk: 'develop-115', + tangem_blockchain_sdk: 'develop-121', // tangem_blockchain_sdk: '0.0.1', ] From fd76b9dccf5a7a8a70ba88f7f488f9885da47fc9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Oct 2022 11:12:26 +0400 Subject: [PATCH 29/32] Updated on 2026-08-14 --- .../details/redux/walletconnect/WalletConnectMiddleware.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index e748673f9e..ed2c68c8d6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -318,9 +318,11 @@ class WalletConnectMiddleware { } else { blockchain } + val derivation = blockchainToMake.derivationPath(store.state.globalState.scanResponse?.card?.derivationStyle) + ?.rawPath val blockchainNetwork = BlockchainNetwork( blockchain = blockchainToMake, - derivationPath = wallet.derivationPath?.rawPath, + derivationPath = derivation, tokens = emptyList(), ) return walletState.getWalletManager(blockchainNetwork) From b89206903dfd74d9cb2f0612d281065a9040e390 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Oct 2022 11:29:48 +0400 Subject: [PATCH 30/32] Updated on 2026-08-14 --- .../addCustomToken/redux/AddCustomTokenHub.kt | 44 ++++++++++++++++--- .../redux/AddCustomTokenState.kt | 36 ++++++++++++--- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 1c09b4116a..dfc524e857 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -5,7 +5,9 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.guard import com.tangem.common.services.Result import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.AddCustomTokenError.Warning.* +import com.tangem.domain.AddCustomTokenError.Warning.PotentialScamToken +import com.tangem.domain.AddCustomTokenError.Warning.TokenAlreadyAdded +import com.tangem.domain.AddCustomTokenError.Warning.UnsupportedSolanaToken import com.tangem.domain.AddCustomTokenException import com.tangem.domain.DomainDialog import com.tangem.domain.DomainWrapped @@ -14,10 +16,40 @@ import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.common.form.* -import com.tangem.domain.features.addCustomToken.* -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.* +import com.tangem.domain.common.form.Field +import com.tangem.domain.common.form.Form +import com.tangem.domain.common.form.TokenContractAddressValidator +import com.tangem.domain.common.form.TokenDecimalsValidator +import com.tangem.domain.common.form.TokenNameValidator +import com.tangem.domain.common.form.TokenNetworkValidator +import com.tangem.domain.common.form.TokenSymbolValidator +import com.tangem.domain.features.addCustomToken.AddCustomTokenService +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.ContractAddress +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Decimals +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.DerivationPath +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Name +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Network +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Symbol +import com.tangem.domain.features.addCustomToken.TokenBlockchainField +import com.tangem.domain.features.addCustomToken.TokenDerivationPathField +import com.tangem.domain.features.addCustomToken.TokenField +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.FieldError +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Init +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnAddCustomTokenClicked +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnCreate +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnDestroy +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenContractAddressChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDecimalsChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDerivationPathChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNameChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNetworkChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenSymbolChanged +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Screen +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.SetFoundTokenInfo +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.UpdateForm +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Warning +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState.Companion.createInitialScreenState import com.tangem.domain.redux.BaseStoreHub import com.tangem.domain.redux.DomainState import com.tangem.domain.redux.ReStoreReducer @@ -569,12 +601,12 @@ private class AddCustomTokenReducer( tangemTechService = globalState.networkServices.tangemTechService, supportedTokenNetworkIds = supportedTokenNetworkIds ) - val form = Form(AddCustomTokenState.createFormFields(card, CustomTokenType.Blockchain)) state.copy( cardDerivationStyle = card.derivationStyle, form = form, tangemTechServiceManager = tangemTechServiceManager, + screenState = createInitialScreenState(card.settings.isHDWalletAllowed), ) } is OnDestroy -> { diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index 97c652da19..2ecd595e8e 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -9,9 +9,31 @@ import com.tangem.domain.DomainWrapped import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.supportedTokens -import com.tangem.domain.common.form.* -import com.tangem.domain.features.addCustomToken.* -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* +import com.tangem.domain.common.form.CustomTokenValidator +import com.tangem.domain.common.form.DataField +import com.tangem.domain.common.form.FieldDataConverter +import com.tangem.domain.common.form.FieldId +import com.tangem.domain.common.form.FieldToJsonConverter +import com.tangem.domain.common.form.Form +import com.tangem.domain.common.form.StringIsEmptyValidator +import com.tangem.domain.common.form.StringIsNotEmptyValidator +import com.tangem.domain.common.form.TokenContractAddressValidator +import com.tangem.domain.common.form.TokenDecimalsValidator +import com.tangem.domain.common.form.TokenNameValidator +import com.tangem.domain.common.form.TokenNetworkValidator +import com.tangem.domain.common.form.TokenSymbolValidator +import com.tangem.domain.features.addCustomToken.AddCustomTokenService +import com.tangem.domain.features.addCustomToken.CustomCurrency +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.ContractAddress +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Decimals +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.DerivationPath +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Name +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Network +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Symbol +import com.tangem.domain.features.addCustomToken.TokenBlockchainField +import com.tangem.domain.features.addCustomToken.TokenDerivationPathField +import com.tangem.domain.features.addCustomToken.TokenField import com.tangem.domain.redux.DomainState import com.tangem.domain.redux.state.StringActionStateConverter import com.tangem.network.api.tangemTech.CoinsResponse @@ -119,7 +141,7 @@ data class AddCustomTokenState( formErrors = emptyMap(), foundToken = null, warnings = emptySet(), - screenState = createInitialScreenState(), + screenState = createInitialScreenState(card.settings.isHDWalletAllowed), tangemTechServiceManager = null, ) } @@ -223,15 +245,15 @@ data class AddCustomTokenState( return (listOf(Blockchain.Unknown) + evmBlockchains).sortByName() } - private fun createInitialScreenState(): ScreenState { + internal fun createInitialScreenState(showDerivationPathField: Boolean = false): ScreenState { return ScreenState( contractAddressField = ViewStates.TokenField(), network = ViewStates.TokenField(), name = ViewStates.TokenField(isEnabled = false), symbol = ViewStates.TokenField(isEnabled = false), decimals = ViewStates.TokenField(isEnabled = false), - derivationPath = ViewStates.TokenField(), - addButton = ViewStates.AddButton(isEnabled = false) + derivationPath = ViewStates.TokenField(isVisible = showDerivationPathField), + addButton = ViewStates.AddButton(isEnabled = false), ) } } From 4e98a7b8059fa135f3ab3f13842c2dbd46e1846f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Oct 2022 12:15:19 +0400 Subject: [PATCH 31/32] Updated on 2026-08-14 --- .../tap/features/wallet/redux/reducers/MultiWalletReducer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 52ab9c4d75..892b5db149 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -65,7 +65,7 @@ class MultiWalletReducer { } val selectedCurrency = if (!state.isMultiwalletAllowed) { - wallets[0].walletsData[0].currency + wallets.firstOrNull()?.walletsData?.firstOrNull()?.currency } else { state.selectedCurrency } From 84fc5613b98ff3e555855195d81af6f3425b9b31 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Oct 2022 14:20:38 +0400 Subject: [PATCH 32/32] Updated on 2026-08-14 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 0b11f515b1..b23c84e56f 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,7 +1,7 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', - tangem_card_sdk : 'develop-159', + tangem_card_sdk : 'develop-163', tangem_blockchain_sdk: 'develop-121', // tangem_blockchain_sdk: '0.0.1', ]