Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-30 14:08:42 +03:00
parent e33bd4d849
commit b98b0f659c
138 changed files with 686 additions and 346 deletions

View file

@ -15,6 +15,8 @@ import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
/**
* Default implementation of factory for creating list of [CryptoCurrency] for selected card
@ -39,7 +41,7 @@ internal class DefaultCardCryptoCurrencyFactory(
val blockchain = Blockchain.fromNetworkId(networkId = network.backendId)
// multi-currency wallet
if (userWallet.isMultiCurrency) {
if (userWallet !is UserWallet.Cold || userWallet.isMultiCurrency) {
return getMultiWalletCurrencies(userWallet = userWallet, networks = setOf(network))[network].orEmpty()
}
@ -62,7 +64,7 @@ internal class DefaultCardCryptoCurrencyFactory(
val blockchain = networkRawId.toBlockchain()
// multi-currency wallet
if (userWallet.isMultiCurrency) {
if (userWallet.isMultiCurrency || userWallet !is UserWallet.Cold) {
return getMultiWalletCurrenciesByRawId(
userWallet = userWallet,
rawIds = setOf(networkRawId),
@ -146,7 +148,7 @@ internal class DefaultCardCryptoCurrencyFactory(
tokens = response.tokens.filter { token ->
networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath }
},
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
.groupBy(CryptoCurrency::network)
}
@ -164,7 +166,7 @@ internal class DefaultCardCryptoCurrencyFactory(
return responseCurrenciesFactory.createCurrencies(
tokens = response.tokens.filter { token -> token.networkId in networkIds },
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
.groupBy { it.network.id.rawId }
}

View file

@ -333,7 +333,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
)
}
data class CreateDefaultForMultiWalletModel(val multiWallet: UserWallet, val expected: List<CryptoCurrency>)
data class CreateDefaultForMultiWalletModel(val multiWallet: UserWallet.Cold, val expected: List<CryptoCurrency>)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ -381,7 +381,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
}
data class CreatePrimaryCurrencyForSingleWalletModel(
val singleWallet: UserWallet,
val singleWallet: UserWallet.Cold,
val expected: Result<CryptoCurrency>,
)
@ -424,7 +424,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
private fun provideTestModels() = listOf(
CreateForSingleWalletWithTokenModel(
singleWalletWithToken = UserWallet(
singleWalletWithToken = UserWallet.Cold(
name = "NODL",
walletId = UserWalletId("011"),
cardsInWallet = setOf(),
@ -457,13 +457,13 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
}
data class CreateForSingleWalletWithTokenModel(
val singleWalletWithToken: UserWallet,
val singleWalletWithToken: UserWallet.Cold,
val isPrimaryTokenExpected: Boolean = false,
val expected: Result<List<CryptoCurrency>>,
)
private fun createMultiWallet(cardId: String? = null, batchId: String? = null): UserWallet {
return UserWallet(
private fun createMultiWallet(cardId: String? = null, batchId: String? = null): UserWallet.Cold {
return UserWallet.Cold(
name = "Wallet 1",
walletId = UserWalletId("011"),
cardsInWallet = setOf(),
@ -483,8 +483,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
)
}
private fun createSingleWallet(batchId: String? = null, addWalletData: Boolean = false): UserWallet {
return UserWallet(
private fun createSingleWallet(batchId: String? = null, addWalletData: Boolean = false): UserWallet.Cold {
return UserWallet.Cold(
name = "Note",
walletId = UserWalletId("011"),
cardsInWallet = setOf(),
@ -515,8 +515,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
)
}
private fun createSingleWalletWithToken(): UserWallet {
return UserWallet(
private fun createSingleWalletWithToken(): UserWallet.Cold {
return UserWallet.Cold(
name = "NODL",
walletId = UserWalletId("011"),
cardsInWallet = setOf(),

View file

@ -27,6 +27,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
@ -96,7 +97,7 @@ internal class DefaultCustomTokensRepository(
networkFactory.create(
networkId = networkId,
derivationPath = derivationPath,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
),
) {
"Network [$networkId] not found while finding token"
@ -152,7 +153,7 @@ internal class DefaultCustomTokensRepository(
networkFactory.create(
networkId = networkId,
derivationPath = derivationPath,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
),
) {
"Network [$networkId] not found while creating coin"
@ -189,7 +190,7 @@ internal class DefaultCustomTokensRepository(
networkFactory.create(
networkId = networkId,
derivationPath = derivationPath,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
),
) {
"Network [$networkId] not found while creating custom token"
@ -249,7 +250,7 @@ internal class DefaultCustomTokensRepository(
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"User wallet [$userWalletId] not found while getting supported networks"
}
val scanResponse = userWallet.scanResponse
val scanResponse = userWallet.requireColdWallet().scanResponse // TODO [REDACTED_TASK_KEY]
Blockchain.entries
.mapNotNull { blockchain ->

View file

@ -33,6 +33,7 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.pagination.BatchFetchResult
import com.tangem.pagination.BatchListSource
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
@ -80,7 +81,7 @@ internal class DefaultManageTokensRepository(
subFetcher = { request, _, isFirstBatchFetching ->
val userWallet = request.params.userWalletId?.let(userWalletsStore::getSyncStrict)
if (userWallet?.scanResponse?.card?.isTestCard == true) {
if (userWallet is UserWallet.Cold && userWallet.scanResponse.card.isTestCard) {
fetchTestnetCurrencies(userWallet, request)
} else {
fetchCurrencies(
@ -143,13 +144,13 @@ internal class DefaultManageTokensRepository(
managedCryptoCurrencyFactory.createWithCustomTokens(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
} else {
managedCryptoCurrencyFactory.create(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
scanResponse = userWallet?.scanResponse,
scanResponse = userWallet?.requireColdWallet()?.scanResponse, // TODO [REDACTED_TASK_KEY]
)
}
@ -179,7 +180,7 @@ internal class DefaultManageTokensRepository(
testnetTokensConfig
},
tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId),
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
return BatchFetchResult.Success(
@ -191,14 +192,16 @@ internal class DefaultManageTokensRepository(
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
userTokensResponseFactory.createUserTokensResponse(
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(
userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
private fun getSupportedBlockchains(userWallet: UserWallet?): List<Blockchain> {
return userWallet?.scanResponse?.let {
it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains)
return (userWallet as? UserWallet.Cold)?.scanResponse?.let {
it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains) // TODO [REDACTED_TASK_KEY]
} ?: Blockchain.entries.filter {
!it.isTestnet() && it !in excludedBlockchains
}
@ -285,6 +288,10 @@ internal class DefaultManageTokensRepository(
userWallet: UserWallet,
blockchain: Blockchain,
): CurrencyUnsupportedState? {
if (userWallet !is UserWallet.Cold) {
return null
}
val canHandleBlockchain = userWallet.scanResponse.card.canHandleBlockchain(
blockchain = blockchain,
cardTypesResolver = userWallet.cardTypesResolver,
@ -302,6 +309,10 @@ internal class DefaultManageTokensRepository(
userWallet: UserWallet,
blockchain: Blockchain,
): CurrencyUnsupportedState.Token? {
if (userWallet !is UserWallet.Cold) {
return null
}
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
val supportedTokens = userWallet.scanResponse.card.supportedTokens(
cardTypesResolver,

View file

@ -24,6 +24,7 @@ import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.pagination.*
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -250,13 +251,13 @@ internal class DefaultMarketsTokenRepository(
cryptoCurrencyFactory.createCoin(
blockchain = blockchain,
extraDerivationPath = null,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
} else {
val currencyNetwork = networkFactory.create(
blockchain = blockchain,
extraDerivationPath = null,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
) ?: return null
cryptoCurrencyFactory.createToken(

View file

@ -11,6 +11,7 @@ import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.utils.eitherOn
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -52,7 +53,7 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor(
},
)
val cardTypesResolver = userWallet.cardTypesResolver
val cardTypesResolver = userWallet.requireColdWallet().cardTypesResolver // TODO [REDACTED_TASK_KEY]
val isWalletSupported = with(cardTypesResolver) {
isMultiwalletAllowed() || isSingleWalletWithToken()
}

View file

@ -5,6 +5,7 @@ import com.tangem.data.networks.store.NetworksStatusesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.networks.multi.MultiNetworkStatusProducer
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -47,7 +48,7 @@ internal class DefaultMultiNetworkStatusProducer @AssistedInject constructor(
val network = networkFactory.create(
networkId = status.id,
derivationPath = status.id.derivationPath,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse,
) ?: return@mapNotNullTo null
NetworkStatus(network = network, value = status.value)

View file

@ -63,7 +63,7 @@ internal class DefaultMultiNetworkStatusFetcherTest {
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
mockkStatic(UserWallet::cardTypesResolver)
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { userWallet.cardTypesResolver } returns cardTypesResolver
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
@ -132,7 +132,7 @@ internal class DefaultMultiNetworkStatusFetcherTest {
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
mockkStatic(UserWallet::cardTypesResolver)
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { userWallet.cardTypesResolver } returns cardTypesResolver
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns false
coEvery { cardTypesResolver.isSingleWalletWithToken() } returns true
@ -193,7 +193,7 @@ internal class DefaultMultiNetworkStatusFetcherTest {
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
mockkStatic(UserWallet::cardTypesResolver)
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { userWallet.cardTypesResolver } returns cardTypesResolver
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
@ -267,7 +267,7 @@ internal class DefaultMultiNetworkStatusFetcherTest {
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
mockkStatic(UserWallet::cardTypesResolver)
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { userWallet.cardTypesResolver } returns cardTypesResolver
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
@ -367,7 +367,7 @@ internal class DefaultMultiNetworkStatusFetcherTest {
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
mockkStatic(UserWallet::cardTypesResolver)
mockkStatic(UserWallet.Cold::cardTypesResolver)
every { userWallet.cardTypesResolver } returns cardTypesResolver
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns false
coEvery { cardTypesResolver.isSingleWalletWithToken() } returns false

View file

@ -26,6 +26,7 @@ import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.domain.nft.repository.NFTRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.features.nft.NFTFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
@ -204,7 +205,7 @@ internal class DefaultNFTRepository @Inject constructor(
networkFactory.create(
blockchain = it,
extraDerivationPath = null,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
}
}

View file

@ -26,6 +26,7 @@ import com.tangem.domain.onramp.model.HotCryptoCurrency
import com.tangem.domain.onramp.repositories.HotCryptoRepository
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.runCatching
@ -90,7 +91,7 @@ internal class DefaultHotCryptoRepository(
?: error("UserWalletId [$userWalletId] not found")
HotCryptoCurrencyConverter(
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
imageHost = it.imageHost,
excludedBlockchains = excludedBlockchains,
)
@ -170,6 +171,10 @@ internal class DefaultHotCryptoRepository(
// TODO: [REDACTED_JIRA]
private fun UserWallet.canHandleHotCrypto(hotToken: HotCryptoResponse.Token): Boolean {
if (this !is UserWallet.Cold) {
return true // TODO [REDACTED_TASK_KEY]
}
val isToken = hotToken.contractAddress != null && hotToken.decimalCount != null
val blockchain = hotToken.networkId?.let { Blockchain.fromNetworkId(it) } ?: return false

View file

@ -60,6 +60,7 @@ import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.toggles.StakingFeatureToggles
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.lib.crypto.BlockchainUtils.isCardano
@ -257,6 +258,11 @@ internal class DefaultStakingRepository(
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
error("Failed to get user wallet")
}
if (userWallet !is UserWallet.Cold) {
return false
}
val blockchainId = cryptoCurrency.network.rawId
return when {
isSolana(blockchainId) -> INVALID_BATCHES_FOR_SOLANA.contains(userWallet.scanResponse.card.batchId)

View file

@ -17,6 +17,7 @@ import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
import com.tangem.domain.staking.model.StakingID
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import timber.log.Timber

View file

@ -30,6 +30,9 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isLocked
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@ -222,7 +225,9 @@ internal class DefaultCurrenciesRepository(
val userWallet = getUserWallet(userWalletId)
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(
userWallet.requireColdWallet().scanResponse,
)
fetchExpressAssetsByNetworkIds(userWalletId, listOf(currency), refresh)
currency
}
@ -233,7 +238,7 @@ internal class DefaultCurrenciesRepository(
refresh: Boolean,
): List<CryptoCurrency> {
return withContext(dispatchers.io) {
val scanResponse = getUserWallet(userWalletId).scanResponse
val scanResponse = getUserWallet(userWalletId).requireColdWallet().scanResponse
val currencies = if (scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = scanResponse)
@ -257,7 +262,7 @@ internal class DefaultCurrenciesRepository(
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
val currency = cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse,
)
.find { it.id == id }
requireNotNull(currency) { "Unable to find currency with provided ID: $id" }
@ -297,7 +302,7 @@ internal class DefaultCurrenciesRepository(
},
)
responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.scanResponse)
responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.requireColdWallet().scanResponse)
}
override suspend fun getMultiCurrencyWalletCachedCurrenciesSync(userWalletId: UserWalletId) =
@ -312,7 +317,7 @@ internal class DefaultCurrenciesRepository(
},
)
responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.scanResponse)
responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.requireColdWallet().scanResponse)
}
override suspend fun getMultiCurrencyWalletCurrency(
@ -337,7 +342,7 @@ internal class DefaultCurrenciesRepository(
responseCurrenciesFactory.createCurrency(
currencyId = id,
response = response,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse,
)
}
@ -369,7 +374,7 @@ internal class DefaultCurrenciesRepository(
it.derivationPath == derivationPath.value
} ?: error("Coin in this network $networkId not found")
val coin = responseCurrenciesFactory.createCurrency(storedCoin, userWallet.scanResponse)
val coin = responseCurrenciesFactory.createCurrency(storedCoin, userWallet.requireColdWallet().scanResponse)
coin as? CryptoCurrency.Coin ?: error("Unable to create currency")
}
@ -488,7 +493,7 @@ internal class DefaultCurrenciesRepository(
token = token,
networkId = networkId,
extraDerivationPath = null,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
) ?: error("Unable to create token")
}
@ -510,7 +515,7 @@ internal class DefaultCurrenciesRepository(
responseCurrenciesFactory.createCurrencies(
response = storedTokens.copy(tokens = filterResponse),
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse,
)
}
} else {
@ -544,8 +549,8 @@ internal class DefaultCurrenciesRepository(
lazyMessage = { "Saved tokens empty. Can not perform add currencies action" },
)
userTokensSaver.push(
userWalletId,
savedCurrencies,
userWalletId = userWalletId,
response = savedCurrencies,
onFailSend = {
throw IllegalStateException("Unable to push tokens")
},
@ -556,7 +561,7 @@ internal class DefaultCurrenciesRepository(
return getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
responseCurrenciesFactory.createCurrencies(
response = storedTokens,
scanResponse = userWallet.scanResponse,
scanResponse = userWallet.requireColdWallet().scanResponse,
)
}
}
@ -579,7 +584,7 @@ internal class DefaultCurrenciesRepository(
private suspend fun fetchTokens(userWallet: UserWallet) {
val userWalletId = userWallet.walletId
val response = if (checkIsEmptyDemoWallet(userWallet)) {
val response = if (userWallet is UserWallet.Cold && checkIsEmptyDemoWallet(userWallet)) {
createDefaultUserTokensResponse(userWallet)
} else {
safeApiCall({ tangemTechApi.getUserTokens(userWalletId.stringValue).bind() }) {
@ -596,7 +601,7 @@ internal class DefaultCurrenciesRepository(
fetchExpressAssetsByNetworkIds(userWalletId, compatibleUserTokensResponse)
}
private suspend fun checkIsEmptyDemoWallet(userWallet: UserWallet): Boolean {
private suspend fun checkIsEmptyDemoWallet(userWallet: UserWallet.Cold): Boolean {
val response = getSavedUserTokensResponseSync(key = userWallet.walletId)
return demoConfig.isDemoCardId(userWallet.cardId) && response == null
@ -659,7 +664,9 @@ internal class DefaultCurrenciesRepository(
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
userTokensResponseFactory.createUserTokensResponse(
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(
userWallet.requireColdWallet().scanResponse,
),
isGroupedByNetwork = false,
isSortedByBalance = false,
)

View file

@ -24,6 +24,7 @@ import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.domain.visa.repository.VisaRepository
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@ -142,7 +143,7 @@ internal class DefaultVisaRepository @Inject constructor(
txDetailsFactory.create(
transaction = transaction,
walletBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain(),
walletBlockchain = userWallet.requireColdWallet().scanResponse.cardTypesResolver.getBlockchain(),
)
}
}
@ -153,7 +154,7 @@ internal class DefaultVisaRepository @Inject constructor(
val customerInfo = visaApiRequestMaker.request(userWalletId) { authHeader, _ ->
visaApi.getCustomerInfo(
authHeader = authHeader,
cardId = userWallet.scanResponse.card.cardId,
cardId = userWallet.requireColdWallet().scanResponse.card.cardId,
)
}
@ -198,7 +199,7 @@ internal class DefaultVisaRepository @Inject constructor(
}
private fun makeWalletAddresses(userWallet: UserWallet): Set<Address> {
val walletBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
val walletBlockchain = userWallet.requireColdWallet().scanResponse.cardTypesResolver.getBlockchain()
return walletBlockchain.makeAddresses(getCardPubKey(userWallet).hexToBytes())
}
@ -206,7 +207,7 @@ internal class DefaultVisaRepository @Inject constructor(
private fun getCardPubKey(userWallet: UserWallet): String {
if (VisaConstants.IS_DEMO_MODE_ENABLED) return getDemoPublicKey()
val cardWallet = userWallet.scanResponse.card.wallets.firstOrNull {
val cardWallet = userWallet.requireColdWallet().scanResponse.card.wallets.firstOrNull {
it.curve == EllipticCurve.Secp256k1
}
requireNotNull(cardWallet) { "Visa card wallet not found" }
@ -218,7 +219,7 @@ internal class DefaultVisaRepository @Inject constructor(
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"No user wallet found: $userWalletId"
}
if (!userWallet.scanResponse.cardTypesResolver.isVisaWallet()) {
if (!userWallet.requireColdWallet().scanResponse.cardTypesResolver.isVisaWallet()) {
error("VISA wallet required: $userWalletId")
}

View file

@ -15,6 +15,7 @@ import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.visa.model.getAuthHeader
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
@ -52,7 +53,7 @@ internal class VisaApiRequestMaker @Inject constructor(
it.code == ApiResponseError.HttpException.Code.UNAUTHORIZED
) {
userWalletsStore.update(userWalletId) { userWallet ->
userWallet.copy(
userWallet.requireColdWallet().copy(
scanResponse = userWallet.scanResponse.copy(
visaCardActivationStatus = VisaCardActivationStatus.RefreshTokenExpired,
),
@ -63,7 +64,7 @@ internal class VisaApiRequestMaker @Inject constructor(
}
userWalletsStore.update(userWalletId) { userWallet ->
userWallet.copy(
userWallet.requireColdWallet().copy(
scanResponse = userWallet.scanResponse.copy(
visaCardActivationStatus = VisaCardActivationStatus.Activated(
visaAuthTokens = newTokens,
@ -93,7 +94,8 @@ internal class VisaApiRequestMaker @Inject constructor(
@Throws
private fun getAuthTokens(userWalletId: UserWalletId): VisaAuthTokens {
val userWallet = findVisaUserWallet(userWalletId)
val status = userWallet.scanResponse.visaCardActivationStatus ?: error("Visa card activation status not found")
val status = userWallet.requireColdWallet().scanResponse.visaCardActivationStatus
?: error("Visa card activation status not found")
if (status is VisaCardActivationStatus.RefreshTokenExpired) {
throw RefreshTokenExpiredException()
@ -106,7 +108,7 @@ internal class VisaApiRequestMaker @Inject constructor(
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"No user wallet found: $userWalletId"
}
if (!userWallet.scanResponse.cardTypesResolver.isVisaWallet()) {
if (!userWallet.requireColdWallet().scanResponse.cardTypesResolver.isVisaWallet()) {
error("VISA wallet required: $userWalletId")
}

View file

@ -8,6 +8,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.lib.visa.model.VisaContractInfo
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
@ -33,7 +34,7 @@ internal class VisaCurrencyFactory @Inject constructor(
val currencyNetwork = networkFactory.create(
blockchain = Blockchain.Polygon,
extraDerivationPath = null,
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
derivationStyleProvider = userWallet.requireColdWallet().scanResponse.derivationStyleProvider,
canHandleTokens = true,
) ?: error("Unable to create network for Visa currency")

View file

@ -3,6 +3,7 @@ package com.tangem.data.walletconnect
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.walletconnect.repository.WalletConnectRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext

View file

@ -9,6 +9,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcSessionProposal.ProposalNetwork
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
internal class AssociateNetworksDelegate(

View file

@ -7,6 +7,7 @@ import com.tangem.data.walletconnect.model.CAIP2
import com.tangem.data.walletconnect.model.NamespaceKey
import com.tangem.domain.models.network.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
internal interface WcNamespaceConverter {
@ -25,7 +26,7 @@ internal interface WcNamespaceConverter {
return NetworkFactory(excludedBlockchains).create(
blockchain = blockchain,
extraDerivationPath = null,
scanResponse = wallet.scanResponse,
scanResponse = wallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
}
}

View file

@ -89,6 +89,11 @@ internal class DefaultWalletsRepository(
private suspend fun fetchSeedPhraseNotificationStatus(userWalletId: UserWalletId) {
val userWallet = userWalletsStore.getSyncOrNull(key = userWalletId)
if (userWallet != null && userWallet !is UserWallet.Cold) {
updateNotificationVisibility(id = userWalletId, value = SeedPhraseNotificationsStatus.NOT_NEEDED)
return
}
val status = if (userWallet?.isImported == false) {
Status.NOT_NEEDED
} else {
@ -291,7 +296,7 @@ internal class DefaultWalletsRepository(
override suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>) =
withContext(dispatchers.io) {
val publicKeys = authProvider.getCardsPublicKeys()
val walletsBody = wallets.map { userWallet ->
val walletsBody = wallets.filterIsInstance<UserWallet.Cold>().map { userWallet ->
WalletIdBodyConverter.convert(
userWallet = userWallet,
publicKeys = publicKeys.filterKeys { userWallet.cardsInWallet.contains(it) },

View file

@ -176,12 +176,12 @@ class DefaultWalletsRepositoryTest {
val card2PublicKey = "card2_public_key"
val userWallets = listOf(
mockk<UserWallet> {
mockk<UserWallet.Cold> {
every { cardsInWallet } returns setOf(card1PublicKey)
every { walletId } returns UserWalletId(wallet1Id)
every { name } returns "Wallet 1"
},
mockk<UserWallet> {
mockk<UserWallet.Cold> {
every { cardsInWallet } returns setOf(card2PublicKey)
every { walletId } returns UserWalletId(wallet2Id)
every { name } returns "Wallet 2"

View file

@ -15,7 +15,7 @@ class WalletIdBodyConverterTest {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
val walletName = "Test Wallet"
val userWallet = UserWallet(
val userWallet = UserWallet.Cold(
walletId = walletId,
name = walletName,
cardsInWallet = setOf("card1", "card2"),
@ -55,7 +55,7 @@ class WalletIdBodyConverterTest {
// GIVEN
val walletId = UserWalletId("1234567890abcdef")
val walletName = "Test Wallet"
val userWallet = UserWallet(
val userWallet = UserWallet.Cold(
walletId = walletId,
name = walletName,
cardsInWallet = emptySet(),