Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-23 14:37:50 +03:00
commit 2f84def7cc
1244 changed files with 20573 additions and 7307 deletions

View file

@ -4,8 +4,7 @@ import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
@ -35,14 +34,15 @@ class TapWalletManager(
}
private suspend fun loadUserWalletData(userWallet: UserWallet) {
Analytics.setContext(userWallet.requireColdWallet().scanResponse) // [REDACTED_TASK_KEY]
val scanResponse = userWallet.scanResponse
Analytics.setContext(userWallet)
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
withMainContext {
// Order is important
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
if (userWallet is UserWallet.Cold) {
val scanResponse = userWallet.scanResponse
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
withMainContext {
// Order is important
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
}
}
}
}

View file

@ -17,9 +17,9 @@ import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.currency.CryptoCurrency
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.requireColdWallet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
@ -50,7 +50,7 @@ internal class DefaultDerivationsRepository(
networkFactory.create(
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
extraDerivationPath = null,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = userWallet,
)
},
)
@ -61,7 +61,11 @@ internal class DefaultDerivationsRepository(
userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
}
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
if (userWallet is UserWallet.Hot) {
return
}
userWallet.requireColdWallet()
if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) {
Timber.d("Nothing to derive")
@ -84,14 +88,18 @@ internal class DefaultDerivationsRepository(
): Boolean {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
if (userWallet is UserWallet.Hot) {
return false
}
val derivations =
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY]
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse)
.findByNetworks(
networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) ->
networkFactory.create(
blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null,
extraDerivationPath = extraDerivationPath,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = userWallet,
)
},
)

View file

@ -13,7 +13,7 @@ import com.tangem.common.core.UserCodeRequestPolicy
import com.tangem.domain.card.ResetCardUseCase
import com.tangem.domain.card.ResetCardUserCodeParams
import com.tangem.domain.card.models.ResetCardError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.sdk.api.TangemSdkManager
internal class DefaultResetCardUseCase(

View file

@ -1,47 +0,0 @@
package com.tangem.tap.domain.extensions
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.Artwork
import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
fun CardDTO.signedHashesCount(): Int {
return wallets.sumOf { it.totalSignedHashes ?: 0 }
}
suspend fun CardDTO.getOrLoadCardArtworkUrl(
cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null,
onlineCardVerifier: OnlineCardVerifier,
): String {
fun ifAnyError(): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> {
when (getTwinCardNumber()) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL
TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL
else -> Artwork.DEFAULT_IMG_URL
}
}
}
}
return when (val cardInfoResult = cardInfo ?: onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = cardInfoResult.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
ifAnyError()
} else {
CardArtworksProvider.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> ifAnyError()
}
}

View file

@ -0,0 +1,113 @@
package com.tangem.tap.domain.hot
import com.tangem.common.core.TangemSdkError
import com.tangem.features.hotwallet.HotWalletPasswordRequester
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.exception.WrongPasswordException
import com.tangem.hot.sdk.model.*
import javax.inject.Inject
class HotWalletAccessor @Inject constructor(
private val tangemHotSdk: TangemHotSdk,
private val hotWalletPasswordRequester: HotWalletPasswordRequester,
) {
suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> {
val auth = when (hotWalletId.authType) {
HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth
HotWalletId.AuthType.Password -> requestPassword(false)
HotWalletId.AuthType.Biometry -> HotAuth.Biometry
}
return runCatchingSdkErrors(hotWalletId, auth) {
tangemHotSdk.signHashes(
unlockHotWallet = UnlockHotWallet(
walletId = hotWalletId,
auth = it,
),
dataToSign = dataToSign,
).also {
hotWalletPasswordRequester.dismiss()
}
}
}
private suspend fun <T> runCatchingSdkErrors(
hotWalletId: HotWalletId,
auth: HotAuth,
block: suspend (auth: HotAuth) -> T,
): T {
return runCatchingWrongPassInternal(
originalAuth = auth,
auth = auth,
block = { blockAuth ->
block(blockAuth).also {
// TODO [REDACTED_TASK_KEY] if user has biometry enabled, we set it as the new auth method
if (blockAuth is HotAuth.Password /*&& has biometry enabled */) {
tangemHotSdk.changeAuth(
unlockHotWallet = UnlockHotWallet(
walletId = hotWalletId,
auth = blockAuth,
),
auth = HotAuth.Biometry,
)
}
}
},
)
}
private suspend fun <T> runCatchingWrongPassInternal(
originalAuth: HotAuth,
auth: HotAuth,
block: suspend (auth: HotAuth) -> T,
): T = runCatching {
block(auth)
}.getOrElse { exception ->
if (auth is HotAuth.Biometry && exception.isBiometryError()) {
// fallback to password if biometry fails
val passAuth = requestPassword(true)
return@getOrElse runCatchingWrongPassInternal(
originalAuth = originalAuth,
auth = passAuth,
block = block,
)
}
if (exception !is WrongPasswordException) {
throw exception
}
// If the exception is a wrong password, we need to request the password again
hotWalletPasswordRequester.wrongPassword()
val passResult = requestPassword(originalAuth is HotAuth.Biometry)
runCatchingWrongPassInternal(
originalAuth = originalAuth,
auth = passResult,
block = block,
)
}
private suspend fun requestPassword(hasBiometry: Boolean): HotAuth {
return hotWalletPasswordRequester.requestPassword(hasBiometry).toAuth() ?: throw TangemSdkError.UserCancelled()
}
private fun Throwable.isBiometryError(): Boolean {
return this is TangemSdkError.AuthenticationFailed ||
this is TangemSdkError.AuthenticationCanceled ||
this is TangemSdkError.AuthenticationLockout ||
this is TangemSdkError.AuthenticationUnavailable ||
this is TangemSdkError.AuthenticationAlreadyInProgress ||
this is TangemSdkError.AuthenticationNotInitialized ||
this is TangemSdkError.AuthenticationPermanentLockout
}
private fun HotWalletPasswordRequester.Result.toAuth() = when (this) {
HotWalletPasswordRequester.Result.UseBiometry -> HotAuth.Biometry
HotWalletPasswordRequester.Result.Dismiss -> null
is HotWalletPasswordRequester.Result.EnteredPassword -> this.password
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.tap.domain.hot
import com.tangem.hot.sdk.model.HotAuth
import com.tangem.hot.sdk.model.HotWalletId
interface HotWalletPasswordRequester {
suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password
}

View file

@ -0,0 +1,78 @@
package com.tangem.tap.domain.hot
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.map
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.hot.sdk.model.DataToSign
import com.tangem.operations.sign.SignData
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
class TangemHotSigner @AssistedInject constructor(
@Assisted private val userWallet: UserWallet.Hot,
private val hotWalletAccessor: HotWalletAccessor,
) : TransactionSigner {
override suspend fun sign(hash: ByteArray, publicKey: Wallet.PublicKey): CompletionResult<ByteArray> {
return sign(listOf(hash), publicKey).map { it.first() }
}
override suspend fun sign(
hashes: List<ByteArray>,
publicKey: Wallet.PublicKey,
): CompletionResult<List<ByteArray>> {
val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == publicKey.seedKey }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
val result = hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = listOf(
DataToSign(
curve = wallet.curve,
hashes = hashes,
derivationPath = publicKey.derivationPath,
),
),
)
return CompletionResult.Success(result.map { it.signatures }.flatten())
}
override suspend fun multiSign(
dataToSign: List<SignData>,
publicKey: Wallet.PublicKey,
): CompletionResult<Map<ByteArray, ByteArray>> {
val result = hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = dataToSign.map { signData ->
val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == signData.publicKey }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
DataToSign(
curve = wallet.curve,
hashes = listOf(signData.hash),
derivationPath = signData.derivationPath,
)
},
)
return CompletionResult.Success(
result.mapIndexed { index, data ->
dataToSign[index].publicKey to data.signatures.first()
}.toMap(),
)
}
@AssistedFactory
interface Factory {
fun create(@Assisted userWallet: UserWallet.Hot): TangemHotSigner
}
}

View file

@ -0,0 +1,98 @@
package com.tangem.tap.domain.hot
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.map
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.hot.sdk.model.DataToSign
import com.tangem.operations.sign.SignData
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import timber.log.Timber
class TangemHotWalletSigner @AssistedInject constructor(
@Assisted private val userWallet: UserWallet.Hot,
private val hotWalletAccessor: HotWalletAccessor,
) : TransactionSigner {
override suspend fun sign(hash: ByteArray, publicKey: Wallet.PublicKey): CompletionResult<ByteArray> {
return sign(listOf(hash), publicKey).map { it.first() }
}
override suspend fun sign(
hashes: List<ByteArray>,
publicKey: Wallet.PublicKey,
): CompletionResult<List<ByteArray>> {
val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey.contentEquals(publicKey.seedKey) }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
val result = runCatching {
hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = listOf(
DataToSign(
curve = wallet.curve,
hashes = hashes,
derivationPath = publicKey.derivationPath,
),
),
)
}.getOrElse {
Timber.e(it)
return if (it is TangemSdkError) {
CompletionResult.Failure(it)
} else {
CompletionResult.Failure(TangemSdkError.ExceptionError(it))
}
}
return CompletionResult.Success(result.map { it.signatures }.flatten())
}
override suspend fun multiSign(
dataToSign: List<SignData>,
publicKey: Wallet.PublicKey,
): CompletionResult<Map<ByteArray, ByteArray>> {
val result = runCatching {
hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = dataToSign.map { signData ->
val wallet =
userWallet.wallets.orEmpty().firstOrNull { it.publicKey.contentEquals(signData.publicKey) }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
DataToSign(
curve = wallet.curve,
hashes = listOf(signData.hash),
derivationPath = signData.derivationPath,
)
},
)
}.getOrElse {
Timber.e(it)
return if (it is TangemSdkError) {
CompletionResult.Failure(it)
} else {
CompletionResult.Failure(TangemSdkError.ExceptionError(it))
}
}
return CompletionResult.Success(
result.mapIndexed { index, data ->
dataToSign[index].publicKey to data.signatures.first()
}.toMap(),
)
}
@AssistedFactory
interface Factory {
fun create(@Assisted userWallet: UserWallet.Hot): TangemHotWalletSigner
}
}

View file

@ -23,10 +23,10 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DerivationTaskResponse

View file

@ -17,10 +17,10 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.preflightread.PreflightReadFilter
import com.tangem.operations.wallet.CreateWalletResponse

View file

@ -177,6 +177,13 @@ object WalletMockContent : MockContent {
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),

View file

@ -4,8 +4,8 @@ import com.tangem.common.card.Card
import com.tangem.common.core.SessionEnvironment
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.preflightread.PreflightReadFilter
/**

View file

@ -9,8 +9,8 @@ import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext

View file

@ -4,7 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.CompletionCallback
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.operations.PreflightReadMode
import com.tangem.operations.PreflightReadTask
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter

View file

@ -7,9 +7,6 @@ internal class DefaultTokensFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) : TokensFeatureToggles {
override val isStakingLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "STAKING_LOADING_REFACTORING_ENABLED")
override val isWalletBalanceFetcherEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "WALLET_BALANCE_FETCHER_ENABLED")
}

View file

@ -1,13 +1,12 @@
package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.*
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
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.requireColdWallet
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
@ -213,8 +212,7 @@ internal class BiometricUserWalletsListManager(
changeSelectedUserWallet: Boolean,
canOverridePublicInfo: Boolean,
): CompletionResult<Unit> {
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
val encryptionKey = userWallet.scanResponse.card.encryptionKey
val encryptionKey = userWallet.encryptionKey
?.let { UserWalletEncryptionKey(userWallet.walletId, it) }
?: return CompletionResult.Success(Unit) // No encryption key, no need to save

View file

@ -4,9 +4,9 @@ import com.tangem.common.CompletionResult
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi

View file

@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.model
import com.squareup.moshi.JsonClass
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
@JsonClass(generateAdapter = true)
internal data class UserWalletEncryptionKey(

View file

@ -2,31 +2,44 @@ package com.tangem.tap.domain.userWalletList.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.hot.sdk.model.HotWalletId
@JsonClass(generateAdapter = true)
internal data class UserWalletSensitiveInformation(
// Cold
@Json(name = "wallets")
val wallets: List<CardDTO.Wallet>,
val wallets: List<CardDTO.Wallet>?,
@Json(name = "visaCardActivationStatus")
val visaCardActivationStatus: VisaCardActivationStatus? = null,
// Hot
@Json(name = "mobileWallets")
val mobileWallets: List<MobileWallet>? = null,
)
@JsonClass(generateAdapter = true)
internal data class UserWalletPublicInformation(
// Common
@Json(name = "name")
val name: String,
@Json(name = "walletId")
val walletId: UserWalletId,
// Cold
@Json(name = "cardsInWallet")
val cardsInWallet: Set<String>,
@Json(name = "scanResponse")
val scanResponse: ScanResponse,
val scanResponse: ScanResponse?,
@Json(name = "isMultiCurrency")
val isMultiCurrency: Boolean,
@Json(name = "hasBackupError")
val hasBackupError: Boolean = false,
// Hot
@Json(name = "hotWalletId")
val hotWalletId: HotWalletId? = null,
@Json(name = "backedUp")
val backedUp: Boolean? = null,
)

View file

@ -1,6 +1,6 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
internal interface SelectedUserWalletRepository {
suspend fun get(): UserWalletId?

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
internal interface UserWalletsKeysRepository {

View file

@ -1,8 +1,8 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
internal interface UserWalletsPublicInformationRepository {

View file

@ -1,8 +1,8 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation

View file

@ -10,8 +10,8 @@ import com.tangem.common.services.secure.SecureStorage
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
import kotlinx.coroutines.Dispatchers

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.repository.implementation
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext

View file

@ -7,8 +7,8 @@ import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.common.flatMap
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
import com.tangem.tap.domain.userWalletList.utils.publicInformation

View file

@ -7,8 +7,8 @@ import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.common.services.secure.SecureStorage
import com.tangem.crypto.operations.AESCipherOperations
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation

View file

@ -1,7 +1,8 @@
package com.tangem.tap.domain.userWalletList.utils
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation
@ -10,8 +11,12 @@ internal val UserWallet.sensitiveInformation: UserWalletSensitiveInformation
is UserWallet.Cold -> UserWalletSensitiveInformation(
wallets = scanResponse.card.wallets,
visaCardActivationStatus = scanResponse.visaCardActivationStatus,
mobileWallets = null,
)
is UserWallet.Hot -> UserWalletSensitiveInformation(
wallets = null,
mobileWallets = this.wallets,
)
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
}
internal val UserWallet.publicInformation: UserWalletPublicInformation
@ -28,19 +33,39 @@ internal val UserWallet.publicInformation: UserWalletPublicInformation
visaCardActivationStatus = null,
),
hasBackupError = hasBackupError,
hotWalletId = null,
backedUp = null,
)
is UserWallet.Hot -> UserWalletPublicInformation(
name = name,
walletId = walletId,
isMultiCurrency = isMultiCurrency,
cardsInWallet = emptySet(),
scanResponse = null,
hotWalletId = hotWalletId,
backedUp = backedUp,
)
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
}
internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
return UserWallet.Cold(
name = name,
walletId = walletId,
cardsInWallet = cardsInWallet,
scanResponse = scanResponse,
isMultiCurrency = isMultiCurrency,
hasBackupError = hasBackupError,
)
return if (hotWalletId != null) {
UserWallet.Hot(
name = name,
walletId = walletId,
hotWalletId = hotWalletId,
wallets = null,
backedUp = backedUp!!,
)
} else {
UserWallet.Cold(
name = name,
walletId = walletId,
cardsInWallet = cardsInWallet,
scanResponse = scanResponse!!,
isMultiCurrency = isMultiCurrency,
hasBackupError = hasBackupError,
)
}
}
internal fun List<UserWalletPublicInformation>.toUserWallets(): List<UserWallet> {
@ -53,13 +78,15 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
copy(
scanResponse = scanResponse.copy(
card = scanResponse.card.copy(
wallets = sensitiveInformation.wallets,
wallets = sensitiveInformation.wallets!!,
),
visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
),
)
}
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
is UserWallet.Hot -> copy(
wallets = sensitiveInformation.mobileWallets,
)
}
}
@ -90,5 +117,5 @@ internal fun UserWallet.lock(): UserWallet = when (this) {
),
)
}
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
is UserWallet.Hot -> copy(wallets = null)
}

View file

@ -1,11 +1,16 @@
package com.tangem.tap.domain.userWalletList.utils
import com.tangem.common.extensions.calculateSha256
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.common.extensions.calculateHmacSha256
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWallet
internal val CardDTO.encryptionKey: ByteArray?
get() = findPublicKey(wallets)?.let { calculateEncryptionKey(it) }
internal val UserWallet.encryptionKey: ByteArray?
get() = when (this) {
is UserWallet.Cold -> findPublicKey(this.scanResponse.card.wallets)
is UserWallet.Hot -> findPublicKey(this.wallets.orEmpty())
}?.let { calculateEncryptionKey(it) }
private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray {
val message = MESSAGE_FOR_ENCRYPTION_KEY.toByteArray()
@ -15,8 +20,12 @@ private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray {
}
private fun findPublicKey(wallets: List<CardDTO.Wallet>): ByteArray? {
return wallets.firstOrNull()
?.publicKey
return wallets.firstOrNull()?.publicKey
}
@JvmName("findPublicKeyInMobileWallets")
private fun findPublicKey(wallets: List<MobileWallet>): ByteArray? {
return wallets.firstOrNull()?.publicKey
}
private const val MESSAGE_FOR_ENCRYPTION_KEY = "UserWalletEncryptionKey"

View file

@ -17,7 +17,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.MemoType
import com.tangem.data.walletconnect.network.ethereum.LegacySdkHelper
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.operations.sign.SignHashCommand
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.safeUpdate

View file

@ -8,12 +8,12 @@ import com.reown.android.relay.ConnectionType
import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.WalletKit
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.walletconnect.pair.unsupportedDApps
import com.tangem.data.walletconnect.pair.UnsupportedDApps
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
@ -216,7 +216,7 @@ internal class DefaultLegacyWalletConnectRepository(
Timber.i("sessionProposal: $sessionProposal")
this@DefaultLegacyWalletConnectRepository.sessionProposal = sessionProposal
if (sessionProposal.name in unsupportedDApps) {
if (sessionProposal.name in UnsupportedDApps.list) {
Timber.i("Unsupported DApp")
scope.launch {
_events.emit(

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.walletconnect2.domain
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.walletconnect2.domain.models.*
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
import kotlinx.coroutines.flow.Flow

View file

@ -5,16 +5,15 @@ import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.model.legacy.Session
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
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
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.extensions.dispatchOnMain
@ -422,8 +421,7 @@ class WalletConnectInteractor(
}
private fun getCardId(userWallet: UserWallet): String? {
userWallet.requireColdWallet() // [REDACTED_TASK_KEY]
return if (userWallet.scanResponse.card.backupStatus?.isActive != true) {
return if (userWallet is UserWallet.Cold && userWallet.scanResponse.card.backupStatus?.isActive != true) {
userWallet.cardId
} else { // if wallet has backup, any card from wallet can be used to sign
null