Updated on 2026-08-14
This commit is contained in:
commit
b6bf011204
1257 changed files with 31185 additions and 8936 deletions
|
|
@ -37,6 +37,11 @@ dependencies {
|
|||
implementation(tangemDeps.hot.core)
|
||||
// endregion
|
||||
|
||||
/** Other libraries */
|
||||
implementation(platform(deps.firebase.bom))
|
||||
implementation(deps.firebase.analytics)
|
||||
implementation(deps.timber)
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class HotUserWalletBuilder @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) {
|
||||
val allNetworks = Blockchain.entries
|
||||
val allNetworks = Blockchain.entries.filter { it.isTestnet().not() }
|
||||
val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet()
|
||||
val requests = curves.sortedBy { it.ordinal }.map { curve ->
|
||||
val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.domain.wallets.config
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.card.configs.CardConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
||||
class ColdCurvesConfig(cardDTO: CardDTO) : CurvesConfig {
|
||||
|
||||
val cardConfig = CardConfig.createConfig(cardDTO)
|
||||
|
||||
override val mandatoryCurves: List<EllipticCurve>
|
||||
get() = cardConfig.mandatoryCurves
|
||||
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
return cardConfig.primaryCurve(blockchain)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.domain.wallets.config
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
interface CurvesConfig {
|
||||
|
||||
val mandatoryCurves: List<EllipticCurve>
|
||||
|
||||
fun primaryCurve(blockchain: Blockchain): EllipticCurve?
|
||||
}
|
||||
|
||||
val UserWallet.curvesConfig: CurvesConfig
|
||||
get() = when (this) {
|
||||
is UserWallet.Cold -> ColdCurvesConfig(this.scanResponse.card)
|
||||
is UserWallet.Hot -> HotCurvesConfig
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.wallets.config
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.card.configs.Wallet2CardConfig
|
||||
|
||||
data object HotCurvesConfig : CurvesConfig {
|
||||
|
||||
override val mandatoryCurves: List<EllipticCurve>
|
||||
get() = Wallet2CardConfig.mandatoryCurves
|
||||
|
||||
override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
|
||||
return Wallet2CardConfig.primaryCurve(blockchain)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,11 +10,14 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.models.UserWalletRemoteInfo
|
||||
import com.tangem.domain.models.wallet.copy
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class DefaultUserWalletsSyncDelegate(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : UserWalletsSyncDelegate {
|
||||
|
||||
|
|
@ -28,10 +31,43 @@ class DefaultUserWalletsSyncDelegate(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO remove dispatchers whnen UserWalletsListManager will be main safe
|
||||
private suspend fun renameUserWallet(
|
||||
userWalletId: UserWalletId,
|
||||
name: String,
|
||||
): Either<UpdateWalletError, UserWallet> = if (useNewRepository) {
|
||||
renameUserWalletInNewRepository(userWalletId, name)
|
||||
} else {
|
||||
renameUserWalletInLegacyRepository(userWalletId, name)
|
||||
}
|
||||
|
||||
private suspend fun renameUserWalletInNewRepository(
|
||||
userWalletId: UserWalletId,
|
||||
name: String,
|
||||
): Either<UpdateWalletError, UserWallet> = either {
|
||||
val userWallets = userWalletsListRepository.userWalletsSync()
|
||||
val userWallet = userWallets.find { it.walletId == userWalletId }
|
||||
?: raise(UpdateWalletError.DataError(IllegalStateException("User wallet with id $userWalletId not found")))
|
||||
|
||||
ensure(userWallets.none { it.name == name && it.walletId != userWalletId }) {
|
||||
UpdateWalletError.NameAlreadyExists
|
||||
}
|
||||
|
||||
ensure(name != userWallet.name) {
|
||||
UpdateWalletError.NameAlreadyExists
|
||||
}
|
||||
|
||||
val updatedWallet = userWallet.copy(name = name)
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(updatedWallet, canOverride = true)
|
||||
.map { updatedWallet }
|
||||
.mapLeft { error -> UpdateWalletError.DataError(IllegalStateException("")) }
|
||||
.bind()
|
||||
}
|
||||
|
||||
// TODO remove dispatchers whnen UserWalletsListManager will be main safe
|
||||
private suspend fun renameUserWalletInLegacyRepository(
|
||||
userWalletId: UserWalletId,
|
||||
name: String,
|
||||
): Either<UpdateWalletError, UserWallet> = withContext(dispatchers.io) {
|
||||
either {
|
||||
val existingNames = userWalletsListManager.userWalletsSync
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.domain.wallets.derivations
|
||||
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
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.wallets.usecase.BackendId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
interface ColdMapDerivationsRepository {
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(userWallet: UserWallet.Cold, currencies: List<CryptoCurrency>): UserWallet.Cold
|
||||
|
||||
suspend fun derivePublicKeysByNetworkIds(
|
||||
userWallet: UserWallet.Cold,
|
||||
networkIds: List<Network.RawID>,
|
||||
): UserWallet.Cold
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeysByNetworks(userWallet: UserWallet.Cold, networks: List<Network>): UserWallet.Cold
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(
|
||||
userWallet: UserWallet.Cold,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): Pair<UserWallet.Cold, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
||||
|
||||
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
||||
suspend fun hasMissedDerivations(
|
||||
userWallet: UserWallet.Cold,
|
||||
networksWithDerivationPath: Map<BackendId, String?>,
|
||||
): Boolean
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.domain.wallets.derivations
|
||||
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isWallet2
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
||||
interface DerivationStyleProvider {
|
||||
fun getDerivationStyle(): DerivationStyle?
|
||||
}
|
||||
|
||||
internal class TangemDerivationStyleProvider(
|
||||
private val card: CardDTO,
|
||||
) : DerivationStyleProvider {
|
||||
override fun getDerivationStyle(): DerivationStyle? {
|
||||
return when {
|
||||
!card.settings.isHDWalletAllowed -> null
|
||||
firstBatchesOfWallet1(card) -> DerivationStyle.V1
|
||||
card.isWallet2 -> DerivationStyle.V3
|
||||
else -> DerivationStyle.V2
|
||||
}
|
||||
}
|
||||
|
||||
private fun firstBatchesOfWallet1(card: CardDTO): Boolean {
|
||||
return card.batchId == "AC01" || card.batchId == "AC02" || card.batchId == "CB95"
|
||||
}
|
||||
}
|
||||
|
||||
internal class TangemHotDerivationStyleProvider : DerivationStyleProvider {
|
||||
override fun getDerivationStyle(): DerivationStyle? = DerivationStyle.V3
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.wallets.derivations
|
||||
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
val UserWallet.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = when (this) {
|
||||
is UserWallet.Cold -> scanResponse.derivationStyleProvider
|
||||
is UserWallet.Hot -> TangemHotDerivationStyleProvider()
|
||||
}
|
||||
|
||||
val ScanResponse.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = card.derivationStyleProvider
|
||||
|
||||
val CardDTO.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = TangemDerivationStyleProvider(this)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.domain.wallets.derivations
|
||||
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.BackendId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
interface DerivationsRepository {
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||
|
||||
suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.RawID>)
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>)
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(
|
||||
userWalletId: UserWalletId,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): Map<ByteArrayKey, ExtendedPublicKeysMap>
|
||||
|
||||
/** Check if user [userWalletId] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
||||
suspend fun hasMissedDerivations(
|
||||
userWalletId: UserWalletId,
|
||||
networksWithDerivationPath: Map<BackendId, String?>,
|
||||
): Boolean
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.domain.wallets.derivations
|
||||
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
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.wallets.usecase.BackendId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
interface HotMapDerivationsRepository {
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(userWallet: UserWallet.Hot, currencies: List<CryptoCurrency>): UserWallet.Hot
|
||||
|
||||
suspend fun derivePublicKeysByNetworkIds(
|
||||
userWallet: UserWallet.Hot,
|
||||
networkIds: List<Network.RawID>,
|
||||
): UserWallet.Hot
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeysByNetworks(userWallet: UserWallet.Hot, networks: List<Network>): UserWallet.Hot
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeys(
|
||||
userWallet: UserWallet.Hot,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): Pair<UserWallet.Hot, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
||||
|
||||
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
||||
suspend fun hasMissedDerivations(
|
||||
userWallet: UserWallet.Hot,
|
||||
networksWithDerivationPath: Map<BackendId, String?>,
|
||||
): Boolean
|
||||
}
|
||||
|
|
@ -4,16 +4,14 @@ import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.card.common.util.hasDerivation
|
||||
import com.tangem.domain.card.configs.Wallet2CardConfig
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.orEmpty
|
||||
import com.tangem.domain.wallets.config.curvesConfig
|
||||
|
||||
fun UserWallet.hasDerivation(blockchain: Blockchain, derivationPath: String): Boolean {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> scanResponse.hasDerivation(blockchain, derivationPath)
|
||||
is UserWallet.Hot -> {
|
||||
val primaryCurve = Wallet2CardConfig.primaryCurve(blockchain) // TODO [REDACTED_TASK_KEY]: handle hot wallet config
|
||||
val primaryCurve = curvesConfig.primaryCurve(blockchain)
|
||||
val list = if (blockchain == Blockchain.Cardano) {
|
||||
listOf(
|
||||
CardanoUtils.extendedDerivationPath(DerivationPath(derivationPath)),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.domain.wallets.hot
|
||||
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Repository for managing access code attempts for hot wallets.
|
||||
* It tracks the number of attempts made to access a hot wallet and applies cooldowns or deletion
|
||||
* based on the number of attempts.
|
||||
*/
|
||||
interface HotWalletAccessCodeAttemptsRepository {
|
||||
|
||||
/**
|
||||
* Increments the number of attempts for the given [AttemptId].
|
||||
* If the number of attempts exceeds [MAX_FAST_FORWARD_ATTEMPTS], a cooldown period is initiated.
|
||||
*/
|
||||
suspend fun incrementAttempts(id: AttemptId)
|
||||
|
||||
/**
|
||||
* Resets the attempts for the given [HotWalletId].
|
||||
* This is typically called when the user successfully authenticates or when the wallet is deleted.
|
||||
*/
|
||||
suspend fun resetAttempts(hotWalletId: HotWalletId)
|
||||
|
||||
/**
|
||||
* Retrieves the current attempts for the given [AttemptId].
|
||||
* The result is a flow that emits the current state of attempts.
|
||||
*/
|
||||
fun getAttempts(id: AttemptId): Flow<Attempts>
|
||||
|
||||
/**
|
||||
* Synchronously retrieves the current attempts for the given [AttemptId].
|
||||
* This is useful when you need to get the attempts without using a flow.
|
||||
*/
|
||||
suspend fun getAttemptsSync(id: AttemptId): Attempts
|
||||
|
||||
data class AttemptId(
|
||||
val hotWalletId: HotWalletId,
|
||||
val auth: Boolean,
|
||||
)
|
||||
|
||||
sealed interface Attempts {
|
||||
val count: Int
|
||||
|
||||
data class FastForward(
|
||||
override val count: Int,
|
||||
) : Attempts
|
||||
|
||||
data class WithDelay(
|
||||
override val count: Int,
|
||||
val remainingSeconds: Int,
|
||||
) : Attempts
|
||||
|
||||
data class BeforeDeletion(
|
||||
override val count: Int,
|
||||
val remainingSeconds: Int,
|
||||
val remainingAttemptsCountBeforeDeletion: Int,
|
||||
) : Attempts
|
||||
|
||||
data object Deletion : Attempts {
|
||||
override val count: Int = MAX_ATTEMPTS_BEFORE_DELETION
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val COOLDOWN_SECONDS = 60
|
||||
const val MAX_FAST_FORWARD_ATTEMPTS = 5
|
||||
const val ATTEMPTS_BEFORE_DELETION = 20
|
||||
const val MAX_ATTEMPTS_BEFORE_DELETION = 30
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.domain.wallets.hot
|
||||
|
||||
import com.tangem.hot.sdk.model.HotAuth
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
|
||||
/**
|
||||
* Interface for requesting the password for a hot wallet.
|
||||
* It provides methods to handle password requests, authentication states, and user interactions.
|
||||
*/
|
||||
interface HotWalletPasswordRequester {
|
||||
|
||||
/**
|
||||
* Sets state to show wrong password state.
|
||||
*/
|
||||
suspend fun wrongPassword()
|
||||
|
||||
/**
|
||||
* Sets state to show successful authentication state.
|
||||
*/
|
||||
suspend fun successfulAuthentication()
|
||||
|
||||
/**
|
||||
* Requests the user to enter the password for the hot wallet.
|
||||
* @param attemptRequest Contains information about the hot wallet and authentication mode.
|
||||
* @return Result of the password request, which can be either a password entry, biometric use, or dismissal.
|
||||
*/
|
||||
suspend fun requestPassword(attemptRequest: AttemptRequest): Result
|
||||
|
||||
/**
|
||||
* Dismisses the password request dialog.
|
||||
*/
|
||||
suspend fun dismiss()
|
||||
|
||||
/**
|
||||
* Represents a request to authenticate with a hot wallet.
|
||||
* @param hotWalletId The ID of the hot wallet to authenticate with.
|
||||
* @param authMode Indicates whether the request is for authentication mode.
|
||||
* In auth mode user can be deleted after failed attempts.
|
||||
* @param hasBiometry Indicates whether to show biometric authentication option.
|
||||
*/
|
||||
data class AttemptRequest(
|
||||
val hotWalletId: HotWalletId,
|
||||
val authMode: Boolean,
|
||||
val hasBiometry: Boolean,
|
||||
)
|
||||
|
||||
sealed class Result {
|
||||
data object UseBiometry : Result()
|
||||
data object Dismiss : Result()
|
||||
data class EnteredPassword(val password: HotAuth.Password) : Result()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.domain.wallets.models
|
||||
|
||||
sealed interface DeleteWalletError {
|
||||
|
||||
data object UnableToDelete : DeleteWalletError
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.domain.wallets.models
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed interface SaveWalletError {
|
||||
|
||||
val messageId: Int?
|
||||
|
||||
data class DataError(override val messageId: Int?) : SaveWalletError
|
||||
|
||||
data class WalletAlreadySaved(override val messageId: Int) : SaveWalletError
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.domain.wallets.models
|
||||
|
||||
sealed interface SelectWalletError {
|
||||
|
||||
object UnableToSelectUserWallet : SelectWalletError
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.domain.wallets.repository
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
interface HotDerivationsRepository {
|
||||
|
||||
fun getAllSupportedNetworks(): Set<Network>
|
||||
}
|
||||
|
|
@ -13,10 +13,20 @@ interface WalletsRepository {
|
|||
|
||||
suspend fun shouldSaveUserWalletsSync(): Boolean
|
||||
|
||||
@Deprecated("Hot wallet make always save user wallets. Do not use this method")
|
||||
fun shouldSaveUserWallets(): Flow<Boolean>
|
||||
|
||||
@Deprecated("Hot wallet make always save user wallets. Do not use this method")
|
||||
suspend fun saveShouldSaveUserWallets(item: Boolean)
|
||||
|
||||
suspend fun useBiometricAuthentication(): Boolean
|
||||
|
||||
suspend fun setUseBiometricAuthentication(value: Boolean)
|
||||
|
||||
suspend fun requireAccessCode(): Boolean
|
||||
|
||||
suspend fun setRequireAccessCode(value: Boolean)
|
||||
|
||||
suspend fun isWalletWithRing(userWalletId: UserWalletId): Boolean
|
||||
|
||||
suspend fun setHasWalletsWithRing(userWalletId: UserWalletId)
|
||||
|
|
@ -49,6 +59,10 @@ interface WalletsRepository {
|
|||
|
||||
suspend fun setNotificationsEnabled(userWalletId: UserWalletId, isEnabled: Boolean)
|
||||
|
||||
fun isUpgradeWalletNotificationEnabled(userWalletId: UserWalletId): Flow<Boolean>
|
||||
|
||||
suspend fun dismissUpgradeWalletNotification(userWalletId: UserWalletId)
|
||||
|
||||
@Throws
|
||||
suspend fun setWalletName(walletId: String, walletName: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import arrow.core.Either
|
|||
import arrow.core.raise.either
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.DeleteWalletError
|
||||
import com.tangem.domain.core.wallets.error.DeleteWalletError
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
|
||||
/**
|
||||
* Use case for deleting user wallet
|
||||
|
|
@ -14,7 +15,11 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DeleteWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class DeleteWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Deletes user wallet with provided ID.
|
||||
|
|
@ -24,6 +29,12 @@ class DeleteWalletUseCase(private val userWalletsListManager: UserWalletsListMan
|
|||
* @return [Either] with [DeleteWalletError] or [Boolean] which indicates that there are still saved wallets.
|
||||
* */
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Boolean> {
|
||||
if (useNewRepository) {
|
||||
return userWalletsListRepository.delete(userWalletIds = listOf(userWalletId)).map {
|
||||
userWalletsListRepository.selectedUserWallet.value != null
|
||||
}
|
||||
}
|
||||
|
||||
return either {
|
||||
userWalletsListManager.delete(userWalletIds = listOf(userWalletId))
|
||||
.doOnFailure {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
|
||||
class DerivePublicKeysUseCase(
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>): Either<Throwable, Unit> {
|
||||
return Either.Companion.catch {
|
||||
derivationsRepository.derivePublicKeys(userWalletId, currencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
||||
class DismissUpgradeWalletNotificationUseCase(
|
||||
private val walletsRepository: WalletsRepository,
|
||||
) {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId) {
|
||||
walletsRepository.dismissUpgradeWalletNotification(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class GenerateBuyTangemCardLinkUseCase {
|
||||
|
||||
suspend operator fun invoke(): String = suspendCoroutine { cont ->
|
||||
Firebase.analytics.appInstanceId
|
||||
.addOnSuccessListener { id ->
|
||||
cont.resume("$NEW_BUY_WALLET_URL&app_instance_id=$id")
|
||||
}
|
||||
.addOnFailureListener {
|
||||
cont.resume(NEW_BUY_WALLET_URL)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NEW_BUY_WALLET_URL = "https://buy.tangem.com/?utm_source=tangem-app&utm_medium=app"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,16 @@ package com.tangem.domain.wallets.usecase
|
|||
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.core.wallets.requireUserWalletsSync
|
||||
|
||||
/**
|
||||
* Use case for user wallet name generation
|
||||
*/
|
||||
class GenerateWalletNameUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
operator fun invoke(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
|
||||
|
|
@ -17,16 +21,24 @@ class GenerateWalletNameUseCase(
|
|||
isStartToCoin = isStartToCoin,
|
||||
)
|
||||
|
||||
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
|
||||
val existingNames = getNamesSet()
|
||||
return suggestedWalletName(defaultName, existingNames)
|
||||
}
|
||||
|
||||
fun invokeForHot(): String {
|
||||
val defaultName = "Wallet"
|
||||
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
|
||||
val existingNames = getNamesSet()
|
||||
return suggestedWalletName(defaultName, existingNames)
|
||||
}
|
||||
|
||||
private fun getNamesSet(): Set<String> {
|
||||
return if (useNewRepository) {
|
||||
userWalletsListRepository.requireUserWalletsSync().map { it.name }.toSet()
|
||||
} else {
|
||||
userWalletsListManager.userWalletsSync.map { it.name }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun suggestedWalletName(defaultName: String, existingNames: Set<String>): String {
|
||||
val startIndex = 2
|
||||
if (!existingNames.contains(defaultName)) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.calculateRipemd160
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.crypto.NetworkType
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
/**
|
||||
* Derivates an exteneded public key (xpub) based on blockchain hardened derivation
|
||||
*/
|
||||
class GetExtendedPublicKeyForCurrencyUseCase(
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<Throwable, String> {
|
||||
return Either.catch {
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found for userWalletId=$userWalletId and network=$network")
|
||||
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
|
||||
val hdKey = if (isSecp256k1Blockchain) {
|
||||
walletManager.wallet.publicKey.derivationType?.hdKey ?: error("No derivation found")
|
||||
} else {
|
||||
error("No derivation found")
|
||||
}
|
||||
|
||||
var childKey = makeChildKey(
|
||||
isBip44DerivationStyleXPUB = blockchain.isBip44DerivationStyleXPUB(),
|
||||
extendedPublicKey = hdKey.extendedPublicKey,
|
||||
derivationPath = hdKey.path,
|
||||
)
|
||||
|
||||
var parentKey = Key(
|
||||
derivationPath = childKey.derivationPath.dropLastNodes(1),
|
||||
extendedPublicKey = null,
|
||||
)
|
||||
|
||||
val pendingDerivations = getPendingDerivations(childKey, parentKey)
|
||||
val derivedKeys = deriveKeys(
|
||||
userWalletId = userWalletId,
|
||||
seedKey = walletManager.wallet.publicKey.seedKey,
|
||||
paths = pendingDerivations,
|
||||
)
|
||||
|
||||
if (childKey.extendedPublicKey == null) {
|
||||
childKey = childKey.copy(
|
||||
extendedPublicKey = derivedKeys[childKey.derivationPath] ?: error("Failed to derive child key"),
|
||||
)
|
||||
}
|
||||
|
||||
if (parentKey.extendedPublicKey == null) {
|
||||
parentKey = parentKey.copy(
|
||||
extendedPublicKey = derivedKeys[parentKey.derivationPath] ?: error("Failed to derive parent key"),
|
||||
)
|
||||
}
|
||||
|
||||
makeExtendedKey(childKey, parentKey, network.isTestnet)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if xpub generation is supported, false otherwise
|
||||
*/
|
||||
suspend fun isSupported(userWalletId: UserWalletId, network: Network): Either<Throwable, Boolean> = Either.catch {
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found for user wallet $userWalletId and network ${network.id}")
|
||||
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
val isHdKey = walletManager.wallet.publicKey.derivationType?.hdKey
|
||||
|
||||
val isSupported = isSecp256k1Blockchain && isHdKey != null
|
||||
|
||||
return isSupported.right()
|
||||
}
|
||||
|
||||
private suspend fun deriveKeys(
|
||||
userWalletId: UserWalletId,
|
||||
seedKey: ByteArray,
|
||||
paths: MutableList<DerivationPath>,
|
||||
): ExtendedPublicKeysMap {
|
||||
val result = derivationsRepository.derivePublicKeys(userWalletId, mapOf(ByteArrayKey(seedKey) to paths))
|
||||
return result.getValue(ByteArrayKey(seedKey))
|
||||
}
|
||||
|
||||
private fun makeExtendedKey(childKey: Key, parentKey: Key, isTestnet: Boolean): String {
|
||||
val publicKey = childKey.extendedPublicKey?.publicKey ?: error("No public key found")
|
||||
val chainCode = childKey.extendedPublicKey.chainCode
|
||||
val lastChildNode = childKey.derivationPath.nodes.last()
|
||||
val parentPublicKey = parentKey.extendedPublicKey?.publicKey
|
||||
|
||||
val depth = childKey.derivationPath.nodes.size
|
||||
val childNumber = lastChildNode.index
|
||||
val parentFingerprint = parentPublicKey
|
||||
?.calculateSha256()?.calculateRipemd160()
|
||||
?.take(PARENT_FINGERPRINT_SIZE)?.toByteArray()
|
||||
?: error("No parent fingerprint found")
|
||||
|
||||
val net = if (isTestnet) NetworkType.Testnet else NetworkType.Mainnet
|
||||
return ExtendedPublicKey(
|
||||
publicKey = publicKey,
|
||||
chainCode = chainCode,
|
||||
depth = depth,
|
||||
parentFingerprint = parentFingerprint,
|
||||
childNumber = childNumber,
|
||||
).serialize(net)
|
||||
}
|
||||
|
||||
private fun getPendingDerivations(childKey: Key, parentKey: Key): MutableList<DerivationPath> {
|
||||
val pendingDerivations = mutableListOf<DerivationPath>()
|
||||
|
||||
if (childKey.extendedPublicKey == null) {
|
||||
pendingDerivations.add(childKey.derivationPath)
|
||||
}
|
||||
|
||||
if (parentKey.extendedPublicKey == null) {
|
||||
pendingDerivations.add(parentKey.derivationPath)
|
||||
}
|
||||
|
||||
return pendingDerivations
|
||||
}
|
||||
|
||||
private fun makeChildKey(
|
||||
isBip44DerivationStyleXPUB: Boolean,
|
||||
extendedPublicKey: ExtendedPublicKey,
|
||||
derivationPath: DerivationPath,
|
||||
): Key = if (isBip44DerivationStyleXPUB) {
|
||||
Key(derivationPath.dropLastNodes(2), null)
|
||||
} else {
|
||||
Key(derivationPath, extendedPublicKey)
|
||||
}
|
||||
|
||||
private fun DerivationPath.dropLastNodes(count: Int): DerivationPath {
|
||||
return DerivationPath(nodes.dropLast(count))
|
||||
}
|
||||
|
||||
private data class Key(
|
||||
val derivationPath: DerivationPath,
|
||||
val extendedPublicKey: ExtendedPublicKey?,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
const val PARENT_FINGERPRINT_SIZE = 4
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,20 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.domain.wallets.legacy.isLockedSync
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
class GetSavedWalletsCountUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<List<UserWallet>> {
|
||||
if (useNewRepository) {
|
||||
return userWalletsListRepository.userWallets.map { requireNotNull(it) }
|
||||
}
|
||||
|
||||
return userWalletsListManager.savedWalletsCount
|
||||
.filter { count ->
|
||||
if (count == 0) return@filter true
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import arrow.core.raise.ensureNotNull
|
|||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
|
||||
/**
|
||||
* Use case for getting selected wallet.
|
||||
|
|
@ -15,10 +16,20 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GetSelectedWalletSyncUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class GetSelectedWalletSyncUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean = false,
|
||||
) {
|
||||
|
||||
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
|
||||
operator fun invoke(): Either<GetUserWalletError, UserWallet> {
|
||||
if (useNewRepository) {
|
||||
return either {
|
||||
userWalletsListRepository.selectedUserWallet.value ?: raise(GetUserWalletError.UserWalletNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
return either {
|
||||
ensureNotNull(
|
||||
value = userWalletsListManager.selectedUserWalletSync,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import arrow.core.raise.either
|
|||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
|
||||
/**
|
||||
* Use case for getting flow of selected wallet.
|
||||
|
|
@ -14,12 +16,32 @@ import kotlinx.coroutines.flow.Flow
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GetSelectedWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
|
||||
class GetSelectedWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean = false,
|
||||
) {
|
||||
|
||||
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
|
||||
operator fun invoke(): Either<GetUserWalletError, Flow<UserWallet>> {
|
||||
return either {
|
||||
userWalletsListManager.selectedUserWallet
|
||||
if (useNewRepository) {
|
||||
userWalletsListRepository.selectedUserWallet.filterNotNull()
|
||||
} else {
|
||||
userWalletsListManager.selectedUserWallet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
|
||||
fun sync(): Either<GetUserWalletError, UserWallet?> {
|
||||
return either {
|
||||
if (useNewRepository) {
|
||||
userWalletsListRepository.selectedUserWallet.value
|
||||
} else {
|
||||
userWalletsListManager.selectedUserWalletSync
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,13 +10,24 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.core.wallets.requireUserWalletsSync
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
|
||||
class GetUserWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class GetUserWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewListRepository: Boolean,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId): Either<GetUserWalletError, UserWallet> = either {
|
||||
val userWallets = userWalletsListManager.userWalletsSync
|
||||
val userWallets = if (useNewListRepository) {
|
||||
userWalletsListRepository.requireUserWalletsSync()
|
||||
} else {
|
||||
userWalletsListManager.userWalletsSync
|
||||
}
|
||||
|
||||
ensureNotNull(userWallets.firstOrNull { it.walletId == userWalletId }) {
|
||||
raise(GetUserWalletError.UserWalletNotFound)
|
||||
|
|
@ -25,7 +36,13 @@ class GetUserWalletUseCase(private val userWalletsListManager: UserWalletsListMa
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun invokeFlow(userWalletId: UserWalletId): EitherFlow<GetUserWalletError, UserWallet> {
|
||||
return userWalletsListManager.userWallets.transformLatest { userWallets ->
|
||||
val flow = if (useNewListRepository) {
|
||||
userWalletsListRepository.userWallets.map { requireNotNull(it) }
|
||||
} else {
|
||||
userWalletsListManager.userWallets
|
||||
}
|
||||
|
||||
return flow.transformLatest { userWallets ->
|
||||
userWallets.firstOrNull { it.walletId == userWalletId }
|
||||
?.let { emit(it.right()) }
|
||||
?: emit(GetUserWalletError.UserWalletNotFound.left())
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.core.wallets.requireUserWalletsSync
|
||||
|
||||
/**
|
||||
* Use case for getting list of user wallets names.
|
||||
*
|
||||
* @property userWalletsListManager user wallets list manager
|
||||
*/
|
||||
class GetWalletNamesUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class GetWalletNamesUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
operator fun invoke(): List<String> = userWalletsListManager.userWalletsSync.map { it.name }
|
||||
operator fun invoke(): List<String> = if (useNewRepository) {
|
||||
userWalletsListRepository.requireUserWalletsSync().map { it.name }
|
||||
} else {
|
||||
userWalletsListManager.userWalletsSync.map { it.name }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* Use case for getting list of user wallets
|
||||
|
|
@ -11,11 +13,23 @@ import kotlinx.coroutines.flow.Flow
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GetWalletsUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class GetWalletsUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewListRepository: Boolean,
|
||||
) {
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
operator fun invoke(): Flow<List<UserWallet>> = userWalletsListManager.userWallets
|
||||
operator fun invoke(): Flow<List<UserWallet>> = if (useNewListRepository) {
|
||||
userWalletsListRepository.userWallets.map { requireNotNull(it) }
|
||||
} else {
|
||||
userWalletsListManager.userWallets
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun invokeSync(): List<UserWallet> = userWalletsListManager.userWalletsSync
|
||||
fun invokeSync(): List<UserWallet> = if (useNewListRepository) {
|
||||
userWalletsListRepository.userWallets.value!!
|
||||
} else {
|
||||
userWalletsListManager.userWalletsSync
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
|
||||
typealias BackendId = String
|
||||
|
||||
/**
|
||||
* Use case to check if user has missed derivations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class HasMissedDerivationsUseCase(
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
) {
|
||||
|
||||
/** Check if user [userWalletId] has missed derivations using map of [com.tangem.domain.models.network.Network.ID] with extraDerivationPath */
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
networksWithDerivationPath: Map<BackendId, String?>,
|
||||
): Boolean {
|
||||
return derivationsRepository.hasMissedDerivations(userWalletId, networksWithDerivationPath)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.models.scan.CardDTO
|
|||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
|
|
@ -12,12 +13,22 @@ import kotlinx.coroutines.flow.map
|
|||
*
|
||||
* @property userWalletsListManager user wallets list manager
|
||||
*/
|
||||
class IsNeedToBackupUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class IsNeedToBackupUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
operator fun invoke(id: UserWalletId): Flow<Boolean> {
|
||||
return userWalletsListManager.userWallets
|
||||
val userWalletsFlow = if (useNewRepository) {
|
||||
userWalletsListRepository.userWallets
|
||||
} else {
|
||||
userWalletsListManager.userWallets
|
||||
}
|
||||
|
||||
return userWalletsFlow
|
||||
.map { wallets ->
|
||||
val wallet = wallets.firstOrNull { it.walletId == id }
|
||||
val wallet = wallets?.firstOrNull { it.walletId == id }
|
||||
if (wallet == null) {
|
||||
false
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class IsUpgradeWalletNotificationEnabledUseCase(
|
||||
private val walletsRepository: WalletsRepository,
|
||||
) {
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<Boolean> {
|
||||
return walletsRepository.isUpgradeWalletNotificationEnabled(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,12 @@ import arrow.core.raise.either
|
|||
import arrow.core.right
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.core.wallets.error.SaveWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListError
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.SaveWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
||||
/**
|
||||
* Use case for saving user wallet
|
||||
|
|
@ -18,22 +20,60 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SaveWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class SaveWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWallet: UserWallet, canOverride: Boolean = false): Either<SaveWalletError, Unit> {
|
||||
return either {
|
||||
userWalletsListManager.save(userWallet, canOverride)
|
||||
.doOnSuccess { return Unit.right() }
|
||||
.doOnFailure {
|
||||
return when (it) {
|
||||
is UserWalletsListError.WalletAlreadySaved -> SaveWalletError.WalletAlreadySaved(
|
||||
it.messageResId,
|
||||
)
|
||||
else -> SaveWalletError.DataError(it.messageResId)
|
||||
}.left()
|
||||
}
|
||||
return if (useNewRepository) {
|
||||
either {
|
||||
val newUserWallet =
|
||||
userWalletsListRepository.userWalletsSync().none { it.walletId == userWallet.walletId }
|
||||
val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride).bind()
|
||||
|
||||
return Unit.right()
|
||||
if (newUserWallet) {
|
||||
when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
if (walletsRepository.useBiometricAuthentication()) {
|
||||
userWalletsListRepository.setLock(
|
||||
userWallet.walletId,
|
||||
UserWalletsListRepository.LockMethod.Biometric,
|
||||
)
|
||||
} else {
|
||||
Unit.right()
|
||||
}
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
userWalletsListRepository.setLock(
|
||||
userWallet.walletId,
|
||||
UserWalletsListRepository.LockMethod.NoLock,
|
||||
)
|
||||
}
|
||||
}.mapLeft {
|
||||
SaveWalletError.DataError(null)
|
||||
}.map {
|
||||
userWalletsListRepository.select(userWallet.walletId)
|
||||
}.bind()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
either {
|
||||
userWalletsListManager.save(userWallet, canOverride)
|
||||
.doOnSuccess { return Unit.right() }
|
||||
.doOnFailure {
|
||||
return when (it) {
|
||||
is UserWalletsListError.WalletAlreadySaved -> SaveWalletError.WalletAlreadySaved(
|
||||
it.messageResId,
|
||||
)
|
||||
else -> SaveWalletError.DataError(it.messageResId)
|
||||
}.left()
|
||||
}
|
||||
|
||||
return Unit.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@ import arrow.core.Either
|
|||
import arrow.core.raise.either
|
||||
import arrow.core.right
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.core.wallets.error.SelectWalletError
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.SelectWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
|
||||
/**
|
||||
* Use case for selecting wallet
|
||||
|
|
@ -20,10 +21,19 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
*/
|
||||
class SelectWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<SelectWalletError, UserWallet> {
|
||||
if (useNewRepository) {
|
||||
return userWalletsListRepository.select(userWalletId).map {
|
||||
reduxStateHolder.onUserWalletSelected(it)
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
return either {
|
||||
return when (val result = userWalletsListManager.select(userWalletId)) {
|
||||
is CompletionResult.Failure -> raise(SelectWalletError.UnableToSelectUserWallet)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.models.UpdateWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.core.wallets.error.SaveWalletError
|
||||
import com.tangem.domain.wallets.models.UpdateWalletError.*
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
|
||||
/**
|
||||
* Use case for updating user wallet
|
||||
|
|
@ -15,15 +18,38 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class UpdateWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
|
||||
class UpdateWalletUseCase(
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val useNewRepository: Boolean,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
update: suspend (UserWallet) -> UserWallet,
|
||||
): Either<UpdateWalletError, UserWallet> = either {
|
||||
when (val result = userWalletsListManager.update(userWalletId, update)) {
|
||||
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
|
||||
is CompletionResult.Success -> result.data
|
||||
): Either<UpdateWalletError, UserWallet> {
|
||||
if (useNewRepository) {
|
||||
val userWallet = userWalletsListRepository.userWallets.value?.find { it.walletId == userWalletId }
|
||||
?: return Either.Left(
|
||||
UpdateWalletError.DataError(IllegalStateException("User wallet with id $userWalletId not found")),
|
||||
)
|
||||
val updatedWallet = update(userWallet)
|
||||
return userWalletsListRepository.saveWithoutLock(updatedWallet, canOverride = true)
|
||||
.mapLeft {
|
||||
when (it) {
|
||||
is SaveWalletError.DataError -> DataError(
|
||||
IllegalStateException("Failed to update wallet: ${it.messageId}"),
|
||||
)
|
||||
is SaveWalletError.WalletAlreadySaved -> UpdateWalletError.NameAlreadyExists
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return either {
|
||||
when (val result = userWalletsListManager.update(userWalletId, update)) {
|
||||
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
|
||||
is CompletionResult.Success -> result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,11 @@ class GetSavedWalletsCountUseCaseTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
userWalletsListManager = mockk()
|
||||
useCase = GetSavedWalletsCountUseCase(userWalletsListManager)
|
||||
useCase = GetSavedWalletsCountUseCase(
|
||||
userWalletsListManager,
|
||||
userWalletsListRepository = mockk(),
|
||||
useNewRepository = false,
|
||||
)
|
||||
mockkStatic("com.tangem.domain.wallets.legacy.UserWalletsListManagerExtensionsKt")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue