Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-13 15:34:05 +03:00
parent d48eea4fb0
commit a88a63d6ef
22 changed files with 95 additions and 37 deletions

View file

@ -33,7 +33,7 @@ internal object CardDomainModule {
@Provides
@Singleton
fun provideIsDemoCardUseCase(): IsDemoCardUseCase = IsDemoCardUseCase(config = DemoConfig())
fun provideIsDemoCardUseCase(): IsDemoCardUseCase = IsDemoCardUseCase(config = DemoConfig)
@Provides
@Singleton

View file

@ -28,7 +28,7 @@ internal object TransactionDomainModule {
fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase {
return GetFeeUseCase(
walletManagersFacade = walletManagersFacade,
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
)
}
@ -48,7 +48,7 @@ internal object TransactionDomainModule {
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
cardSdkConfigRepository = cardSdkConfigRepository,
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
@ -126,7 +126,7 @@ internal object TransactionDomainModule {
fun provideEstimateFeeUseCase(walletManagersFacade: WalletManagersFacade): EstimateFeeUseCase {
return EstimateFeeUseCase(
walletManagersFacade = walletManagersFacade,
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
)
}

View file

@ -14,10 +14,11 @@ import com.tangem.common.map
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.configs.CardConfig
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.backup.StartPrimaryCardLinkingCommand
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
@ -60,7 +61,13 @@ class CreateProductWalletTask(
val cardDto = CardDTO(card)
val commandProcessor = when {
cardTypesResolver.isTangemNote() -> CreateWalletTangemNote(cardTypesResolver)
/**
* @workaround isDemoNoteAsMultiwallet
* There were produced 20k Note demo cards that should work like multiwallet (except Onboarding)
* for that reasons we've just added some specific checks for their BatchId
*/
DemoConfig.isDemoNoteAsMultiwallet(card.cardId) || cardTypesResolver.isTangemNote() ->
CreateWalletTangemNote(cardTypesResolver)
cardTypesResolver.isTangemTwins() ->
throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
@ -374,7 +381,7 @@ private class CreateWalletTangemWallet(
private fun getBlockchains(cardId: String, card: CardDTO): List<Blockchain> {
return when {
DemoHelper.isDemoCardId(cardId) -> DemoHelper.config.demoBlockchains.toList()
DemoHelper.isDemoCardId(cardId) -> DemoHelper.config.getDemoBlockchains(cardId).toList()
card.isTestCard -> listOf(Blockchain.BitcoinTestnet, Blockchain.EthereumTestnet)
else -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
}

View file

@ -38,7 +38,7 @@ internal class DerivationsFinder(
getBlockchains(userWalletId)
}.ifEmpty {
if (DemoHelper.isDemoCardId(card.cardId)) {
getDemoBlockchains(derivationStyle)
getDemoBlockchains(derivationStyle, card.cardId)
} else {
getDefaultBlockchains(derivationStyle)
}
@ -77,8 +77,8 @@ internal class DerivationsFinder(
}
// TODO: Move to user wallet config
private fun getDemoBlockchains(derivationStyle: DerivationStyle?): MutableSet<BlockchainToDerive> {
return DemoHelper.config.demoBlockchains.mapToBlockchainsWithDerivations(derivationStyle)
private fun getDemoBlockchains(derivationStyle: DerivationStyle?, cardId: String): MutableSet<BlockchainToDerive> {
return DemoHelper.config.getDemoBlockchains(cardId).mapToBlockchainsWithDerivations(derivationStyle)
}
// TODO: Move to user wallet config

View file

@ -5,7 +5,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.redux.AppState
object DemoHelper {
val config = DemoConfig()
val config = DemoConfig
fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId)

View file

@ -56,7 +56,7 @@ class UserTokensResponseFactory @Inject constructor() {
accountId: AccountId?,
): UserTokensResponse {
val tokens = userWallet?.let {
getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig())
getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig)
.map { blockchain ->
val derivationPath = networkFactory.createDerivationPath(
blockchain = blockchain,

View file

@ -40,7 +40,7 @@ internal object DataCommonModule {
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
): CardCryptoCurrencyFactory {
return DefaultCardCryptoCurrencyFactory(
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
excludedBlockchains = excludedBlockchains,
userWalletsStore = userWalletsStore,
accountsFeatureToggles = accountsFeatureToggles,

View file

@ -17,7 +17,7 @@ fun getDefaultWalletBlockchains(userWallet: UserWallet, demoConfig: DemoConfig):
val card = userWallet.scanResponse.card
var blockchainsInternal = if (demoConfig.isDemoCardId(card.cardId)) {
demoConfig.demoBlockchains
demoConfig.getDemoBlockchains(card.cardId)
} else {
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
}

View file

@ -43,7 +43,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
private val walletAccountsFetcher = mockk<WalletAccountsFetcher>()
private val factory = DefaultCardCryptoCurrencyFactory(
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
excludedBlockchains = excludedBlockchains,
userWalletsStore = userWalletsStore,
userTokensResponseStore = userTokensResponseStore,

View file

@ -46,7 +46,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
)
} else {
DefaultMultiWalletCryptoCurrenciesFetcher(
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
userWalletsStore = userWalletsStore,
tangemTechApi = tangemTechApi,
customTokensMerger = CustomTokensMerger(

View file

@ -48,7 +48,7 @@ internal class DefaultCurrenciesRepository(
excludedBlockchains: ExcludedBlockchains,
) : CurrenciesRepository {
private val demoConfig = DemoConfig()
private val demoConfig = DemoConfig
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
private val userTokensResponseFactory = UserTokensResponseFactory()
private val customTokensMerger = CustomTokensMerger(

View file

@ -47,7 +47,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
private val expressServiceFetcher: ExpressServiceFetcher = mockk(relaxUnitFun = true)
private val fetcher = DefaultMultiWalletCryptoCurrenciesFetcher(
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
userWalletsStore = userWalletsStore,
tangemTechApi = tangemTechApi,
customTokensMerger = customTokensMerger,

View file

@ -41,7 +41,7 @@ internal object TransactionDataModule {
fun providesFeeRepository(walletManagersFacade: WalletManagersFacade): FeeRepository {
return DefaultFeeRepository(
walletManagersFacade,
demoConfig = DemoConfig(),
demoConfig = DemoConfig,
)
}

View file

@ -64,7 +64,7 @@ internal class DefaultWalletManagersFacade @Inject constructor(
blockchainSDKFactory: BlockchainSDKFactory,
) : WalletManagersFacade {
private val demoConfig by lazy { DemoConfig() }
private val demoConfig by lazy { DemoConfig }
private val resultFactory by lazy { UpdateWalletManagerResultFactory() }
private val walletManagerFactory by lazy { WalletManagerFactory(blockchainSDKFactory) }
private val sdkTokenConverter by lazy { SdkTokenConverter() }

View file

@ -1,7 +1,6 @@
package com.tangem.domain.card
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Blockchain.Companion.fromId
import com.tangem.blockchain.common.Token
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
@ -12,6 +11,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.common.TapWorkarounds.isWallet2
import com.tangem.domain.card.common.getTwinCardIdForUser
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.operations.attestation.Attestation
@ -23,7 +23,16 @@ internal class TangemCardTypesResolver(
private val walletData: WalletData?,
) : CardTypesResolver {
override fun isTangemNote(): Boolean = productType == ProductType.Note
override fun isTangemNote(): Boolean {
val isNote = productType == ProductType.Note
/**
* @workaround
* There were produced 20k Note demo cards that should work like multiwallet (except Onboarding)
* for that reasons we've just added some specific checks for their BatchId
*/
val isNotDemoNoteAsMulti = !DemoConfig.isDemoNoteAsMultiwallet(card.cardId)
return isNote && isNotDemoNoteAsMulti
}
override fun isTangemWallet(): Boolean {
return card.settings.isBackupAllowed && card.settings.isHDWalletAllowed &&

View file

@ -15,7 +15,6 @@ object TapWorkarounds {
private const val TEST_CARD_BATCH = "99FF"
private const val TEST_CARD_ID_STARTS_WITH = "FF99"
private val backupRequiredFirmwareVersion = FirmwareVersion(major = 6, minor = 21)
private val demoConfig = DemoConfig()
val CardDTO.isTangemTwins: Boolean
get() = TwinsHelper.getTwinCardNumber(cardId) != null
@ -34,7 +33,7 @@ object TapWorkarounds {
// for cards 6.21 and higher backup is not skippable
val CardDTO.canSkipBackup: Boolean
get() = this.firmwareVersion < backupRequiredFirmwareVersion || demoConfig.isDemoCardId(cardId)
get() = this.firmwareVersion < backupRequiredFirmwareVersion || DemoConfig.isDemoCardId(cardId)
val CardDTO.hasOldStyleDerivation: Boolean
get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95"

View file

@ -5,15 +5,28 @@ import com.tangem.blockchain.common.Blockchain
import java.math.BigDecimal
@Suppress("LargeClass")
class DemoConfig {
object DemoConfig {
val demoBlockchains = setOf(
/**
* @workaround
* There were produced 20k Note demo cards that should work like multiwallet (except Onboarding)
* for that reasons we've just added some specific checks for their BatchId
*/
private const val DEMO_NOTE_AS_MILTIWALLET_BATCH = "DE00"
private val demoBlockchains = setOf(
Blockchain.Bitcoin,
Blockchain.Ethereum,
Blockchain.Dogecoin,
Blockchain.Solana,
)
private val demoBlockchainsSecpOnly = setOf(
Blockchain.Bitcoin,
Blockchain.Ethereum,
Blockchain.Dogecoin,
)
private val demoCardIds: List<String> by lazy {
val demoIds = getReleaseIds().toMutableList()
if (BuildConfig.DEBUG) demoIds.addAll(debugTestDemoCardIds)
@ -28,13 +41,26 @@ class DemoConfig {
Blockchain.Solana to Amount(13.246.toBigDecimal(), Blockchain.Solana),
)
fun isDemoCardId(cardId: String): Boolean = demoCardIds.contains(cardId)
fun isDemoCardId(cardId: String): Boolean = demoCardIds.contains(cardId) ||
cardId.startsWith(DEMO_NOTE_AS_MILTIWALLET_BATCH)
fun isTestDemoCardId(cardId: String): Boolean = testDemoCardIds.contains(cardId)
fun getBalance(blockchain: Blockchain): Amount = walletBalances[blockchain]?.copy()
?: Amount(BigDecimal.ZERO, blockchain).copy()
fun isDemoNoteAsMultiwallet(cardId: String): Boolean {
return cardId.startsWith(DEMO_NOTE_AS_MILTIWALLET_BATCH)
}
fun getDemoBlockchains(cardId: String): Set<Blockchain> {
return if (cardId.startsWith(DEMO_NOTE_AS_MILTIWALLET_BATCH)) {
demoBlockchainsSecpOnly
} else {
demoBlockchains
}
}
private fun getReleaseIds(): List<String> {
return (releaseDemoCardIds + testDemoCardIds).distinct()
}

View file

@ -30,6 +30,7 @@ dependencies {
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.notifications.models)
implementation(projects.domain.demo.models)
// endregion
// region Tangem libraries

View file

@ -1,7 +1,6 @@
package com.tangem.domain.wallets.builder
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
@ -16,9 +15,6 @@ class ColdUserWalletBuilder @AssistedInject constructor(
private var backupCardsIds: Set<String> = emptySet()
private var hasBackupError: Boolean = false
private val CardDTO.isBackupNotAllowed: Boolean
get() = !settings.isBackupAllowed
/**
* DANGEROUS!!!
* [backupCardsIds] will be non-empty list if card is backed up on current device.
@ -44,8 +40,8 @@ class ColdUserWalletBuilder @AssistedInject constructor(
UserWallet.Cold(
walletId = it,
name = generateWalletNameUseCase(
card = card,
productType = productType,
isBackupNotAllowed = card.isBackupNotAllowed,
isStartToCoin = cardTypesResolver.isStart2Coin(),
),
cardsInWallet = backupCardsIds.plus(card.cardId),

View file

@ -1,5 +1,7 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.requireUserWalletsSync
import com.tangem.domain.models.scan.ProductType
@ -14,10 +16,14 @@ class GenerateWalletNameUseCase(
private val useNewRepository: Boolean,
) {
operator fun invoke(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
private val CardDTO.isBackupNotAllowed: Boolean
get() = !settings.isBackupAllowed
operator fun invoke(productType: ProductType, card: CardDTO, isStartToCoin: Boolean): String {
val defaultName = getDefaultName(
productType = productType,
isBackupNotAllowed = isBackupNotAllowed,
card = card,
isBackupNotAllowed = card.isBackupNotAllowed,
isStartToCoin = isStartToCoin,
)
@ -55,7 +61,20 @@ class GenerateWalletNameUseCase(
return defaultName
}
private fun getDefaultName(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
private fun getDefaultName(
productType: ProductType,
card: CardDTO,
isBackupNotAllowed: Boolean,
isStartToCoin: Boolean,
): String {
/**
* @workaround
* There were produced 20k Note demo cards that should work like multiwallet (except Onboarding)
* for that reasons we've just added some specific checks for their BatchId
*/
if (DemoConfig.isDemoNoteAsMultiwallet(card.cardId)) {
return "Wallet"
}
return when (productType) {
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"

View file

@ -44,7 +44,7 @@ class HasSingleWalletSignedHashesUseCase @Inject constructor(
private fun UserWallet.Cold.isCorrectCardType(): Boolean {
return with(scanResponse.cardTypesResolver) {
!DemoConfig().isDemoCardId(cardId) && isReleaseFirmwareType() && !isMultiwalletAllowed() && !isTangemTwins()
!DemoConfig.isDemoCardId(cardId) && isReleaseFirmwareType() && !isMultiwalletAllowed() && !isTangemTwins()
}
}
}

View file

@ -52,6 +52,7 @@ internal class WalletImageResolver @Inject constructor(
cardTypesResolver.isTangemTwins() -> R.drawable.ill_twins_120_106
cardTypesResolver.isStart2Coin() -> R.drawable.ill_start2coin_120_106
cardTypesResolver.isTangemNote() -> noteImage?.imageResId
DemoConfig.isDemoNoteAsMultiwallet(userWallet.cardId) -> R.drawable.ill_wallet2_cards2_120_106
else -> null
}
}
@ -84,7 +85,7 @@ internal class WalletImageResolver @Inject constructor(
@DrawableRes twoBackupResId: Int = R.drawable.ill_wallet2_cards3_120_106,
): Int? {
return resolveWalletWithBackups { count ->
if (DemoConfig().isDemoCardId(cardId)) return@resolveWalletWithBackups oneBackupResId
if (DemoConfig.isDemoCardId(cardId)) return@resolveWalletWithBackups oneBackupResId
when (count) {
WALLET_WITH_ONE_BACKUP_COUNT -> oneBackupResId