Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-30 10:57:51 +03:00
parent 03f4f51fc7
commit 2f91afa922
67 changed files with 623 additions and 371 deletions

View file

@ -1,34 +0,0 @@
package com.tangem.tap.di.data
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.card.DefaultDerivationsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CardDataModule {
@Singleton
@Provides
fun providesDerivationsRepository(
tangemSdkManager: TangemSdkManager,
userWalletsStore: UserWalletsStore,
networkFactory: NetworkFactory,
dispatchers: CoroutineDispatcherProvider,
): DerivationsRepository {
return DefaultDerivationsRepository(
tangemSdkManager = tangemSdkManager,
userWalletsStore = userWalletsStore,
networkFactory = networkFactory,
dispatchers = dispatchers,
)
}
}

View file

@ -2,11 +2,14 @@ package com.tangem.tap.di.domain
import com.tangem.domain.card.* import com.tangem.domain.card.*
import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.sdk.api.TangemSdkManager import com.tangem.sdk.api.TangemSdkManager

View file

@ -1,6 +1,6 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.managetokens.* import com.tangem.domain.managetokens.*
import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.CustomTokensRepository
import com.tangem.domain.managetokens.repository.ManageTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository

View file

@ -1,7 +1,7 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.markets.* import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher

View file

@ -1,5 +1,6 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.data.wallets.hot.TangemHotWalletSigner
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
@ -11,7 +12,6 @@ import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.TransactionRepository import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.usecase.* import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.tap.domain.hot.TangemHotWalletSigner
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

View file

@ -1,8 +1,6 @@
package com.tangem.tap.di.hot package com.tangem.tap.di.hot
import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.tap.domain.hot.HotWalletPasswordRequester
import com.tangem.tap.features.hot.DefaultHotWalletPasswordRequester
import com.tangem.tap.features.hot.TangemHotSDKProxy import com.tangem.tap.features.hot.TangemHotSDKProxy
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
@ -17,8 +15,4 @@ internal interface TangemHotSdkModule {
@Binds @Binds
@Singleton @Singleton
fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk
@Binds
@Singleton
fun bindHotWalletPasswordRequester(impl: DefaultHotWalletPasswordRequester): HotWalletPasswordRequester
} }

View file

@ -1,9 +0,0 @@
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

@ -1,78 +0,0 @@
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

@ -20,7 +20,7 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId

View file

@ -14,7 +14,7 @@ import com.tangem.common.map
import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isTestCard import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.card.configs.CardConfig
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO

View file

@ -6,7 +6,7 @@ import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.local.token.UserTokensResponseStore import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.card.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId

View file

@ -14,14 +14,14 @@ import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvDecoder
import com.tangem.crypto.CryptoUtils import com.tangem.crypto.CryptoUtils
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isExcluded import com.tangem.domain.card.common.TapWorkarounds.isExcluded
import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.common.TapWorkarounds.isVisa import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.common.TwinsHelper import com.tangem.domain.common.TwinsHelper
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.card.configs.CardConfig
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO

View file

@ -16,7 +16,7 @@ import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError import com.tangem.core.error.ext.tangemError
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation

View file

@ -1,13 +0,0 @@
package com.tangem.tap.features.hot
import com.tangem.hot.sdk.model.HotAuth
import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.tap.domain.hot.HotWalletPasswordRequester
import javax.inject.Inject
class DefaultHotWalletPasswordRequester @Inject constructor() : HotWalletPasswordRequester {
override suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password {
return HotAuth.Password("TODO [REDACTED_TASK_KEY]".toCharArray()) // TODO [REDACTED_TASK_KEY]
}
}

View file

@ -8,8 +8,8 @@ import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.common.test.domain.card.MockScanResponseFactory import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.common.test.domain.wallet.MockUserWalletFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.GenericCardConfig
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network

View file

@ -6,11 +6,11 @@ import com.tangem.blockchain.common.FeePaidCurrency
import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.domain.card.DerivationStyleProvider
import com.tangem.domain.card.common.extensions.canHandleToken import com.tangem.domain.card.common.extensions.canHandleToken
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject

View file

@ -7,10 +7,10 @@ import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.common.test.domain.wallet.MockUserWalletFactory
import com.tangem.common.test.utils.ProvideTestModels import com.tangem.common.test.utils.ProvideTestModels
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.GenericCardConfig
import com.tangem.domain.card.configs.MultiWalletCardConfig import com.tangem.domain.card.configs.MultiWalletCardConfig
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet

View file

@ -19,6 +19,7 @@ dependencies {
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.manageTokens) implementation(projects.domain.manageTokens)
implementation(projects.domain.card) implementation(projects.domain.card)
implementation(projects.domain.wallets)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
implementation(projects.domain.legacy) implementation(projects.domain.legacy)

View file

@ -22,8 +22,8 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.CustomTokensRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network 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.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -247,7 +247,22 @@ internal class DefaultCustomTokensRepository(
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"User wallet [$userWalletId] not found while getting supported networks" "User wallet [$userWalletId] not found while getting supported networks"
} }
val scanResponse = userWallet.requireColdWallet().scanResponse // TODO [REDACTED_TASK_KEY]
when (userWallet) {
is UserWallet.Hot -> {
Blockchain.entries.mapNotNull {
// TODO: refactor [REDACTED_JIRA]\
if (it.isTestnet() || it in excludedBlockchains) return@mapNotNull null
networkFactory.create(
blockchain = it,
extraDerivationPath = null,
userWallet = userWallet,
)
}
}
is UserWallet.Cold -> {
val scanResponse = userWallet.scanResponse
Blockchain.entries Blockchain.entries
.mapNotNull { blockchain -> .mapNotNull { blockchain ->
@ -268,6 +283,8 @@ internal class DefaultCustomTokensRepository(
} }
} }
} }
}
}
override fun createDerivationPath(rawPath: String): Network.DerivationPath { override fun createDerivationPath(rawPath: String): Network.DerivationPath {
val sdkPath = DerivationPath(rawPath) val sdkPath = DerivationPath(rawPath)

View file

@ -13,14 +13,14 @@ import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.datasource.api.tangemTech.models.CoinsResponse
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
import com.tangem.domain.card.DerivationStyleProvider
import com.tangem.domain.card.common.extensions.canHandleToken import com.tangem.domain.card.common.extensions.canHandleToken
import com.tangem.domain.card.common.util.derivationStyleProvider
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import timber.log.Timber import timber.log.Timber
internal class ManagedCryptoCurrencyFactory( internal class ManagedCryptoCurrencyFactory(

View file

@ -22,6 +22,7 @@ dependencies {
/** Project - Domain */ /** Project - Domain */
implementation(projects.domain.visa) implementation(projects.domain.visa)
implementation(projects.domain.card) implementation(projects.domain.card)
implementation(projects.domain.wallets)
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
implementation(projects.domain.appCurrency.models) implementation(projects.domain.appCurrency.models)

View file

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

View file

@ -7,10 +7,10 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.walletmanager.extensions.makePublicKey import com.tangem.data.walletmanager.extensions.makePublicKey
import com.tangem.data.walletmanager.extensions.makeWalletManagerForApp import com.tangem.data.walletmanager.extensions.makeWalletManagerForApp
import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import timber.log.Timber import timber.log.Timber
internal class WalletManagerFactory( internal class WalletManagerFactory(

View file

@ -11,9 +11,14 @@ android {
} }
dependencies { dependencies {
implementation(projects.data.common)
/** Tangem libraries */ /** Tangem libraries */
implementation(tangemDeps.blockchain) // android-library implementation(tangemDeps.blockchain)
implementation(tangemDeps.card.core)
implementation(tangemDeps.hot.core)
implementation(projects.libs.tangemSdkApi)
implementation(projects.libs.blockchainSdk)
/** Core */ /** Core */
implementation(projects.core.datasource) implementation(projects.core.datasource)
@ -21,6 +26,7 @@ dependencies {
/** Domain */ /** Domain */
implementation(projects.domain.wallets) implementation(projects.domain.wallets)
implementation(projects.domain.card)
api(projects.domain.models) api(projects.domain.models)
/** Domain models */ /** Domain models */
@ -29,15 +35,17 @@ dependencies {
/** DI */ /** DI */
implementation(deps.hilt.android) implementation(deps.hilt.android)
implementation(project(":domain:legacy"))
kapt(deps.hilt.kapt) kapt(deps.hilt.kapt)
/** Other deps */ /** Other deps */
implementation(deps.androidx.datastore) implementation(deps.androidx.datastore)
implementation(deps.arrow.core) implementation(deps.arrow.core)
implementation(deps.kotlin.coroutines)
implementation(deps.timber)
/** tests */ /** tests */
testImplementation(projects.domain.models) testImplementation(projects.domain.models)
testImplementation(projects.common.test)
testImplementation(deps.test.junit) testImplementation(deps.test.junit)
testImplementation(deps.test.coroutine) testImplementation(deps.test.coroutine)
testImplementation(deps.test.truth) testImplementation(deps.test.truth)

View file

@ -1,51 +1,50 @@
package com.tangem.tap.domain.card package com.tangem.data.wallets.cold
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.common.network.NetworkFactory import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.data.wallets.derivations.Derivations
import com.tangem.domain.card.BackendId import com.tangem.data.wallets.derivations.MissedDerivationsFinder
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.BackendId
import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.sdk.api.TangemSdkManager import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject
internal typealias Derivations = Map<ByteArrayKey, List<DerivationPath>>
private typealias DerivedKeys = Map<ByteArrayKey, ExtendedPublicKeysMap> private typealias DerivedKeys = Map<ByteArrayKey, ExtendedPublicKeysMap>
internal class DefaultDerivationsRepository( internal class DefaultColdMapDerivationsRepository @Inject constructor(
private val tangemSdkManager: TangemSdkManager, private val tangemSdkManager: TangemSdkManager,
private val userWalletsStore: UserWalletsStore,
private val networkFactory: NetworkFactory, private val networkFactory: NetworkFactory,
private val dispatchers: CoroutineDispatcherProvider, private val dispatchers: CoroutineDispatcherProvider,
) : DerivationsRepository { ) : ColdMapDerivationsRepository {
override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) { override suspend fun derivePublicKeys(
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network)) userWallet: UserWallet.Cold,
currencies: List<CryptoCurrency>,
): UserWallet.Cold = withContext(dispatchers.io) {
derivePublicKeysByNetworks(userWallet = userWallet, networks = currencies.map(CryptoCurrency::network))
} }
override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.RawID>) { override suspend fun derivePublicKeysByNetworkIds(
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") userWallet: UserWallet.Cold,
networkIds: List<Network.RawID>,
): UserWallet.Cold = withContext(dispatchers.io) {
derivePublicKeysByNetworks( derivePublicKeysByNetworks(
userWalletId = userWalletId, userWallet = userWallet,
networks = networkIds.mapNotNull { networks = networkIds.mapNotNull {
networkFactory.create( networkFactory.create(
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
@ -56,44 +55,55 @@ internal class DefaultDerivationsRepository(
) )
} }
override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>) { override suspend fun derivePublicKeysByNetworks(
val userWallet = withContext(dispatchers.io) { userWallet: UserWallet.Cold,
userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") networks: List<Network>,
} ): UserWallet.Cold = withContext(dispatchers.io) {
if (userWallet is UserWallet.Hot) {
return
}
userWallet.requireColdWallet()
if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) { if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) {
Timber.d("Nothing to derive") Timber.d("Nothing to derive")
return return@withContext userWallet
} }
val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse) val derivations = MissedDerivationsFinder(userWallet)
.findByNetworks(networks) .findByNetworks(networks)
.ifEmpty { .ifEmpty {
Timber.d("Nothing to derive") Timber.d("Nothing to derive")
return return@withContext userWallet
} }
derivePublicKeys(userWalletId = userWalletId, derivations = derivations) return@withContext derivePublicKeys(userWallet = userWallet, derivations = derivations).first
}
override suspend fun derivePublicKeys(
userWallet: UserWallet.Cold,
derivations: Map<ByteArrayKey, List<DerivationPath>>,
): Pair<UserWallet.Cold, Map<ByteArrayKey, ExtendedPublicKeysMap>> = withContext(dispatchers.io) {
// todo replace it in task [REDACTED_JIRA]
val preflightReadFilter = UserWalletIdPreflightReadFilter(userWallet.walletId)
val result = tangemSdkManager.derivePublicKeys(
cardId = null,
derivations = derivations,
preflightReadFilter = preflightReadFilter,
)
when (result) {
is CompletionResult.Success -> {
userWallet.updateDerivedKeys(result.data.entries).also {
validateDerivations(scanResponse = it.scanResponse, derivations = derivations)
} to result.data.entries
}
is CompletionResult.Failure -> {
throw result.error
}
}
} }
override suspend fun hasMissedDerivations( override suspend fun hasMissedDerivations(
userWalletId: UserWalletId, userWallet: UserWallet.Cold,
networksWithDerivationPath: Map<BackendId, String?>, networksWithDerivationPath: Map<BackendId, String?>,
): Boolean { ): Boolean = withContext(dispatchers.io) {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
if (userWallet is UserWallet.Hot) {
return false
}
val derivations = val derivations =
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse) MissedDerivationsFinder(userWallet)
.findByNetworks( .findByNetworks(
networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) -> networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) ->
networkFactory.create( networkFactory.create(
@ -104,28 +114,7 @@ internal class DefaultDerivationsRepository(
}, },
) )
return derivations.isNotEmpty() derivations.isNotEmpty()
}
override suspend fun derivePublicKeys(userWalletId: UserWalletId, derivations: Derivations): DerivedKeys {
// todo replace it in task [REDACTED_JIRA]
val preflightReadFilter = UserWalletIdPreflightReadFilter(userWalletId)
tangemSdkManager.derivePublicKeys(
cardId = null,
derivations = derivations,
preflightReadFilter = preflightReadFilter,
).doOnSuccess { response ->
updatePublicKeys(userWalletId = userWalletId, keys = response.entries)
.doOnSuccess {
// TODO [REDACTED_TASK_KEY]
validateDerivations(scanResponse = it.requireColdWallet().scanResponse, derivations = derivations)
return response.entries
}
.doOnFailure { throw it }
}
.doOnFailure { throw it }
error("This code should never be reached")
} }
/** /**
@ -144,16 +133,7 @@ internal class DefaultDerivationsRepository(
} }
} }
private suspend fun updatePublicKeys(userWalletId: UserWalletId, keys: DerivedKeys): CompletionResult<UserWallet> { private fun UserWallet.Cold.updateDerivedKeys(keys: DerivedKeys): UserWallet.Cold {
return withContext(dispatchers.io) {
userWalletsStore.update(
userWalletId = userWalletId,
update = { userWallet -> userWallet.requireColdWallet().updateDerivedKeys(keys) }, // TODO [REDACTED_TASK_KEY]
)
}
}
private fun UserWallet.Cold.updateDerivedKeys(keys: DerivedKeys): UserWallet {
return copy( return copy(
scanResponse = scanResponse.copy( scanResponse = scanResponse.copy(
derivedKeys = getUpdatedDerivedKeys(oldKeys = scanResponse.derivedKeys, newKeys = keys), derivedKeys = getUpdatedDerivedKeys(oldKeys = scanResponse.derivedKeys, newKeys = keys),

View file

@ -0,0 +1,25 @@
package com.tangem.data.wallets.cold
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.operations.preflightread.PreflightReadFilter
/**
* [PreflightReadFilter] for checking if card has expected user wallet id
*
[REDACTED_AUTHOR]
*/
class UserWalletIdPreflightReadFilter(private val expectedUserWalletId: UserWalletId) : PreflightReadFilter {
override fun onCardRead(card: Card, environment: SessionEnvironment) = Unit
override fun onFullCardRead(card: Card, environment: SessionEnvironment) {
val actualUserWalletId = UserWalletIdBuilder.card(card = CardDTO(card)).build() ?: return
if (expectedUserWalletId != actualUserWalletId) throw TangemSdkError.WalletNotFound()
}
}

View file

@ -0,0 +1,95 @@
package com.tangem.data.wallets.derivations
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.map
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository
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.wallets.usecase.BackendId
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultDerivationsRepository @Inject constructor(
private val userWalletsStore: UserWalletsStore,
private val hotDerivationsRepository: HotMapDerivationsRepository,
private val coldDerivationsRepository: ColdMapDerivationsRepository,
private val dispatchers: CoroutineDispatcherProvider,
) : DerivationsRepository {
override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network))
}
override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.RawID>) {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
when (userWallet) {
is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds)
is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds)
}.also {
userWallet.update(it)
}
}
override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>) {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
when (userWallet) {
is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeysByNetworks(userWallet, networks)
is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeysByNetworks(userWallet, networks)
}.also {
userWallet.update(it)
}
}
override suspend fun derivePublicKeys(
userWalletId: UserWalletId,
derivations: Map<ByteArrayKey, List<DerivationPath>>,
): Map<ByteArrayKey, ExtendedPublicKeysMap> {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
return when (userWallet) {
is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeys(userWallet, derivations)
is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeys(userWallet, derivations)
}.let {
userWallet.update(it.first)
it.second
}
}
override suspend fun hasMissedDerivations(
userWalletId: UserWalletId,
networksWithDerivationPath: Map<BackendId, String?>,
): Boolean {
return when (val userWallet = userWalletsStore.getSyncStrict(userWalletId)) {
is UserWallet.Cold -> coldDerivationsRepository.hasMissedDerivations(userWallet, networksWithDerivationPath)
is UserWallet.Hot -> hotDerivationsRepository.hasMissedDerivations(userWallet, networksWithDerivationPath)
}
}
private suspend fun UserWallet.update(newUserWallet: UserWallet) = withContext(dispatchers.io) {
check(this@update.walletId == newUserWallet.walletId) {
"Cannot update UserWallet with different walletId: ${newUserWallet.walletId}"
}
if (this@update == newUserWallet) {
return@withContext // No update needed
}
val updateResult = userWalletsStore.update(
userWalletId = newUserWallet.walletId,
update = { userWalletToUpdate -> newUserWallet },
)
when (updateResult) {
is CompletionResult.Failure -> throw updateResult.error
is CompletionResult.Success -> updateResult.data
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.card package com.tangem.data.wallets.derivations
import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.blockchains.cardano.CardanoUtils
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
@ -8,23 +8,26 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.card.configs.CardConfig
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.card.configs.Wallet2CardConfig
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.KeyWalletPublicKey import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.derivation.ExtendedPublicKeysMap
import kotlin.collections.forEach
private typealias DerivationData = Pair<ByteArrayKey, List<DerivationPath>> private typealias DerivationData = Pair<ByteArrayKey, List<DerivationPath>>
internal typealias Derivations = Map<ByteArrayKey, List<DerivationPath>>
/** /**
* Finder of missed derivations * Finder of missed derivations
* *
* @property scanResponse scanning response * @property userWallet User wallet to find derivations for
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { internal class MissedDerivationsFinder(private val userWallet: UserWallet) {
/** Find missed derivations for given currencies [currencies] */ /** Find missed derivations for given currencies [currencies] */
fun find(currencies: List<CryptoCurrency>): Derivations { fun find(currencies: List<CryptoCurrency>): Derivations {
@ -48,30 +51,39 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
} }
private fun List<Network>.mapToNewDerivations(): List<DerivationData> { private fun List<Network>.mapToNewDerivations(): List<DerivationData> {
val config = CardConfig.createConfig(scanResponse.card) val config = when (userWallet) {
is UserWallet.Cold -> CardConfig.createConfig(userWallet.scanResponse.card)
is UserWallet.Hot -> Wallet2CardConfig // TODO create config [REDACTED_TASK_KEY]
}
return mapNotNull { network -> return mapNotNull { network ->
val blockchain = network.toBlockchain() val blockchain = network.toBlockchain()
val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null
findNewDerivations(curve = curve, scanResponse = scanResponse, network = network) val walletPublicKey = when (userWallet) {
is UserWallet.Cold -> {
val wallet = userWallet.scanResponse.card.wallets.firstOrNull { it.curve == curve }
wallet?.publicKey
}
is UserWallet.Hot -> {
val wallet = userWallet.wallets?.firstOrNull { it.curve == curve }
wallet?.publicKey
} }
} }
private fun findNewDerivations( walletPublicKey?.let {
curve: EllipticCurve, findNewDerivations(curve = curve, publicKey = it, network = network)
scanResponse: ScanResponse, }
network: Network, }
): DerivationData? { }
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val publicKey = wallet.publicKey.toMapKey()
private fun findNewDerivations(curve: EllipticCurve, publicKey: ByteArray, network: Network): DerivationData? {
val derivationCandidates = network val derivationCandidates = network
.getDerivationCandidates(curve) .getDerivationCandidates(curve)
.ifEmpty { return null } .ifEmpty { return null }
.filterAlreadyDerivedKeys(publicKey) .filterAlreadyDerivedKeys(publicKey.toMapKey())
.ifEmpty { return null } .ifEmpty { return null }
return publicKey to derivationCandidates return publicKey.toMapKey() to derivationCandidates
} }
private fun Network.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> { private fun Network.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> {
@ -88,7 +100,7 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? { private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? {
return if (getSupportedCurves().contains(curve)) { return if (getSupportedCurves().contains(curve)) {
derivationPath(style = scanResponse.derivationStyleProvider.getDerivationStyle()) derivationPath(style = userWallet.derivationStyleProvider.getDerivationStyle())
} else { } else {
null null
} }
@ -118,7 +130,15 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
} }
private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> { private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> {
val extendedPublicKeysMap = scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap()) val extendedPublicKeysMap = when (userWallet) {
is UserWallet.Cold -> userWallet.scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap())
is UserWallet.Hot -> {
val wallets = userWallet.wallets ?: return emptyList()
wallets.firstOrNull { it.publicKey.contentEquals(publicKey.bytes) }?.derivedKeys
?: ExtendedPublicKeysMap(emptyMap())
}
}
return extendedPublicKeysMap.keys.toList() return extendedPublicKeysMap.keys.toList()
} }
} }

View file

@ -2,14 +2,21 @@ package com.tangem.data.wallets.di
import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository
import com.tangem.data.wallets.DefaultWalletsRepository import com.tangem.data.wallets.DefaultWalletsRepository
import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository
import com.tangem.data.wallets.derivations.DefaultDerivationsRepository
import com.tangem.data.wallets.hot.DefaultHotMapDerivationsRepository
import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.datastore.RuntimeStateStore
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -45,3 +52,20 @@ internal object WalletsDataModule {
return DefaultWalletNamesMigrationRepository(appPreferencesStore) return DefaultWalletNamesMigrationRepository(appPreferencesStore)
} }
} }
@Module
@InstallIn(SingletonComponent::class)
internal interface WalletsDataBindsModule {
@Binds
@Singleton
fun bindDerivationsRepository(impl: DefaultDerivationsRepository): DerivationsRepository
@Binds
@Singleton
fun bindHotMapDerivationsRepository(impl: DefaultHotMapDerivationsRepository): HotMapDerivationsRepository
@Binds
@Singleton
fun bindColdMapDerivationsRepository(impl: DefaultColdMapDerivationsRepository): ColdMapDerivationsRepository
}

View file

@ -0,0 +1,139 @@
package com.tangem.data.wallets.hot
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.wallets.derivations.MissedDerivationsFinder
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.derivations.HotMapDerivationsRepository
import com.tangem.domain.wallets.usecase.BackendId
import com.tangem.hot.sdk.model.DeriveWalletRequest
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
internal class DefaultHotMapDerivationsRepository @Inject constructor(
private val networkFactory: NetworkFactory,
private val hotWalletAccessor: HotWalletAccessor,
private val dispatchers: CoroutineDispatcherProvider,
) : HotMapDerivationsRepository {
override suspend fun derivePublicKeys(
userWallet: UserWallet.Hot,
currencies: List<CryptoCurrency>,
): UserWallet.Hot {
return derivePublicKeysByNetworks(userWallet = userWallet, networks = currencies.map(CryptoCurrency::network))
}
override suspend fun derivePublicKeysByNetworkIds(
userWallet: UserWallet.Hot,
networkIds: List<Network.RawID>,
): UserWallet.Hot {
return derivePublicKeysByNetworks(
userWallet = userWallet,
networks = networkIds.mapNotNull {
networkFactory.create(
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
extraDerivationPath = null,
userWallet = userWallet,
)
},
)
}
override suspend fun derivePublicKeysByNetworks(
userWallet: UserWallet.Hot,
networks: List<Network>,
): UserWallet.Hot = withContext(dispatchers.default) {
val derivations = MissedDerivationsFinder(userWallet)
.findByNetworks(networks)
.ifEmpty {
Timber.d("Nothing to derive")
return@withContext userWallet
}
derivePublicKeys(userWallet, derivations).first
}
override suspend fun derivePublicKeys(
userWallet: UserWallet.Hot,
derivations: Map<ByteArrayKey, List<DerivationPath>>,
): Pair<UserWallet.Hot, Map<ByteArrayKey, ExtendedPublicKeysMap>> {
val wallets = userWallet.wallets ?: return userWallet to emptyMap()
val request = DeriveWalletRequest(
derivations.map { entry ->
val wallet = wallets.first { it.publicKey.contentEquals(entry.key.bytes) }
DeriveWalletRequest.Request(
curve = wallet.curve,
paths = entry.value,
)
},
)
val result = hotWalletAccessor.derivePublicKeys(
hotWalletId = userWallet.hotWalletId,
request = request,
)
val newKeys =
result.responses.associate { ByteArrayKey(it.seedKey.publicKey) to ExtendedPublicKeysMap(it.publicKeys) }
return userWallet.updateWithNewKeys(newKeys) to newKeys
}
override suspend fun hasMissedDerivations(
userWallet: UserWallet.Hot,
networksWithDerivationPath: Map<BackendId, String?>,
): Boolean = withContext(dispatchers.default) {
val derivations = MissedDerivationsFinder(userWallet)
.findByNetworks(
networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) ->
networkFactory.create(
blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null,
extraDerivationPath = extraDerivationPath,
userWallet = userWallet,
)
},
)
derivations.isNotEmpty()
}
private fun UserWallet.Hot.updateWithNewKeys(newKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>): UserWallet.Hot {
val wallets = this.wallets ?: return this
val derivedKeys = wallets.associate {
it.publicKey.toMapKey() to ExtendedPublicKeysMap(it.derivedKeys)
}
val updatedKeys = getUpdatedDerivedKeys(
oldKeys = derivedKeys,
newKeys = newKeys,
)
return copy(
wallets = wallets.map { wallet ->
wallet.copy(
derivedKeys = updatedKeys[wallet.publicKey.toMapKey()] ?: ExtendedPublicKeysMap(emptyMap()),
)
},
)
}
private fun getUpdatedDerivedKeys(
oldKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
newKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
): Map<ByteArrayKey, ExtendedPublicKeysMap> {
return (oldKeys.keys + newKeys.keys).toSet()
.associateWith { walletKey ->
val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey] ?: emptyMap())
val newDerivations = newKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
ExtendedPublicKeysMap(oldDerivations + newDerivations)
}
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.hot package com.tangem.data.wallets.hot
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.features.hotwallet.HotWalletPasswordRequester import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.exception.WrongPasswordException import com.tangem.hot.sdk.exception.WrongPasswordException
import com.tangem.hot.sdk.model.* import com.tangem.hot.sdk.model.*
@ -12,7 +12,17 @@ class HotWalletAccessor @Inject constructor(
private val hotWalletPasswordRequester: HotWalletPasswordRequester, private val hotWalletPasswordRequester: HotWalletPasswordRequester,
) { ) {
suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> { suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> =
hotSdkRequest(hotWalletId) { unlock ->
tangemHotSdk.signHashes(unlockHotWallet = unlock, dataToSign = dataToSign)
}
suspend fun derivePublicKeys(hotWalletId: HotWalletId, request: DeriveWalletRequest): DerivedPublicKeyResponse =
hotSdkRequest(hotWalletId) { unlock ->
tangemHotSdk.derivePublicKey(unlockHotWallet = unlock, request = request)
}
private suspend fun <T> hotSdkRequest(hotWalletId: HotWalletId, block: suspend (unlock: UnlockHotWallet) -> T): T {
val auth = when (hotWalletId.authType) { val auth = when (hotWalletId.authType) {
HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth
HotWalletId.AuthType.Password -> requestPassword(false) HotWalletId.AuthType.Password -> requestPassword(false)
@ -20,13 +30,7 @@ class HotWalletAccessor @Inject constructor(
} }
return runCatchingSdkErrors(hotWalletId, auth) { return runCatchingSdkErrors(hotWalletId, auth) {
tangemHotSdk.signHashes( block(UnlockHotWallet(hotWalletId, it)).also {
unlockHotWallet = UnlockHotWallet(
walletId = hotWalletId,
auth = it,
),
dataToSign = dataToSign,
).also {
hotWalletPasswordRequester.dismiss() hotWalletPasswordRequester.dismiss()
} }
} }

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.hot package com.tangem.data.wallets.hot
import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.Wallet

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.card package com.tangem.data.wallets.derivations
import android.annotation.SuppressLint import android.annotation.SuppressLint
import com.google.common.truth.Truth import com.google.common.truth.Truth
@ -7,6 +7,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.test.domain.card.MockScanResponseFactory import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.data.common.network.NetworkFactory import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.ScanCardException import com.tangem.domain.card.ScanCardException
import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.GenericCardConfig
@ -14,7 +15,7 @@ import com.tangem.domain.card.configs.MultiWalletCardConfig
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.operations.derivation.DerivationTaskResponse import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager import com.tangem.sdk.api.TangemSdkManager
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerify import io.mockk.coVerify
@ -27,13 +28,17 @@ import org.junit.Test
*/ */
internal class DefaultDerivationsRepositoryTest { internal class DefaultDerivationsRepositoryTest {
private val tangemSdkManager = mockk<DefaultTangemSdkManager>() private val tangemSdkManager = mockk<TangemSdkManager>()
private val userWalletsStore = mockk<UserWalletsStore>() private val userWalletsStore = mockk<UserWalletsStore>()
private val repository = DefaultDerivationsRepository( private val repository = DefaultDerivationsRepository(
tangemSdkManager = tangemSdkManager,
userWalletsStore = userWalletsStore, userWalletsStore = userWalletsStore,
dispatchers = TestingCoroutineDispatcherProvider(), dispatchers = TestingCoroutineDispatcherProvider(),
hotDerivationsRepository = mockk(),
coldDerivationsRepository = DefaultColdMapDerivationsRepository(
tangemSdkManager = tangemSdkManager,
networkFactory = NetworkFactory(excludedBlockchains = ExcludedBlockchains()), networkFactory = NetworkFactory(excludedBlockchains = ExcludedBlockchains()),
dispatchers = TestingCoroutineDispatcherProvider(),
),
) )
private val defaultUserWalletId = UserWalletId("011") private val defaultUserWalletId = UserWalletId("011")
@ -48,7 +53,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test @Test
fun `error if userWalletId not found`() = runTest { fun `error if userWalletId not found`() = runTest {
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns null coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } throws IllegalStateException()
runCatching { runCatching {
repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList())
@ -56,7 +61,7 @@ internal class DefaultDerivationsRepositoryTest {
.onSuccess { error("Should throws exception") } .onSuccess { error("Should throws exception") }
.onFailure { Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java) } .onFailure { Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java) }
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) }
} }
@ -64,13 +69,17 @@ internal class DefaultDerivationsRepositoryTest {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
@Test @Test
fun `success if card is not supported derivations`() = runTest { fun `success if card is not supported derivations`() = runTest {
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns defaultUserWallet coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns defaultUserWallet
runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) } repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList())
runCatching { }
.onSuccess { Truth.assertThat(it) } .onSuccess { Truth.assertThat(it) }
.onFailure { error("Should returns success") } .onFailure {
error("Should returns success")
}
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) }
} }
@ -81,13 +90,13 @@ internal class DefaultDerivationsRepositoryTest {
val userWallet = defaultUserWallet.copy( val userWallet = defaultUserWallet.copy(
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
) )
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet
runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) } runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) }
.onSuccess { Truth.assertThat(it) } .onSuccess { Truth.assertThat(it) }
.onFailure { error("Should returns success") } .onFailure { error("Should returns success") }
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) }
} }
@ -102,7 +111,7 @@ internal class DefaultDerivationsRepositoryTest {
), ),
) )
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet
runCatching { runCatching {
repository.derivePublicKeys( repository.derivePublicKeys(
@ -113,7 +122,7 @@ internal class DefaultDerivationsRepositoryTest {
.onSuccess { Truth.assertThat(it) } .onSuccess { Truth.assertThat(it) }
.onFailure { error("Should returns success") } .onFailure { error("Should returns success") }
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) }
} }
@ -123,7 +132,7 @@ internal class DefaultDerivationsRepositoryTest {
val userWallet = defaultUserWallet.copy( val userWallet = defaultUserWallet.copy(
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
) )
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled
runCatching { runCatching {
@ -135,7 +144,7 @@ internal class DefaultDerivationsRepositoryTest {
.onSuccess { error("Should throws exception") } .onSuccess { error("Should throws exception") }
.onFailure { Truth.assertThat(it).isInstanceOf(ScanCardException.UserCancelled::class.java) } .onFailure { Truth.assertThat(it).isInstanceOf(ScanCardException.UserCancelled::class.java) }
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) }
} }
@ -146,7 +155,7 @@ internal class DefaultDerivationsRepositoryTest {
val userWallet = defaultUserWallet.copy( val userWallet = defaultUserWallet.copy(
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
) )
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success( coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success(
DerivationTaskResponse(DerivedKeysMocks.ethereumDerivedKeys), DerivationTaskResponse(DerivedKeysMocks.ethereumDerivedKeys),
) )
@ -161,7 +170,7 @@ internal class DefaultDerivationsRepositoryTest {
.onSuccess { Truth.assertThat(it) } .onSuccess { Truth.assertThat(it) }
.onFailure { error("Should returns success but $it") } .onFailure { error("Should returns success but $it") }
coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) }
coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) }
coVerify(exactly = 1) { userWalletsStore.update(defaultUserWalletId, any()) } coVerify(exactly = 1) { userWalletsStore.update(defaultUserWalletId, any()) }
} }

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.card package com.tangem.data.wallets.derivations
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationConfigV2 import com.tangem.blockchain.common.derivation.DerivationConfigV2

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.card package com.tangem.data.wallets.derivations
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.blockchains.cardano.CardanoUtils
@ -13,7 +13,7 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.GenericCardConfig
import com.tangem.domain.card.configs.MultiWalletCardConfig import com.tangem.domain.card.configs.MultiWalletCardConfig
import com.tangem.domain.card.configs.Wallet2CardConfig import com.tangem.domain.card.configs.Wallet2CardConfig
import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.wallets.derivations.derivationStyleProvider
import org.junit.Test import org.junit.Test
/** /**
@ -24,7 +24,8 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `empty derivations for empty currencies`() { fun `empty derivations for empty currencies`() {
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(userWallet)
val actual = finder.find(emptyList()) val actual = finder.find(emptyList())
@ -36,7 +37,7 @@ internal class MissedDerivationsFinderTest {
// Bls is not supported // Bls is not supported
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).chia.let(::listOf) val currencies = MockCryptoCurrencyFactory(userWallet).chia.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
@ -58,7 +59,7 @@ internal class MissedDerivationsFinderTest {
) )
} }
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).chiaAndEthereum val currencies = MockCryptoCurrencyFactory(userWallet).chiaAndEthereum
val actual = finder.find(currencies) val actual = finder.find(currencies)
@ -73,7 +74,7 @@ internal class MissedDerivationsFinderTest {
fun `derivations for custom token`() { fun `derivations for custom token`() {
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()) val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).ethereumTokenWithBinanceDerivation val currencies = MockCryptoCurrencyFactory(userWallet).ethereumTokenWithBinanceDerivation
val actual = finder.find(currencies) val actual = finder.find(currencies)
@ -91,7 +92,7 @@ internal class MissedDerivationsFinderTest {
fun `derivations for cardano`() { fun `derivations for cardano`() {
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()) val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).cardano.let(::listOf) val currencies = MockCryptoCurrencyFactory(userWallet).cardano.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
@ -117,7 +118,7 @@ internal class MissedDerivationsFinderTest {
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
) )
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).ethereum.let(::listOf) val currencies = MockCryptoCurrencyFactory(userWallet).ethereum.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
@ -132,7 +133,7 @@ internal class MissedDerivationsFinderTest {
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
) )
val userWallet = MockUserWalletFactory.create(scanResponse) val userWallet = MockUserWalletFactory.create(scanResponse)
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(userWallet)
val currencies = MockCryptoCurrencyFactory(userWallet).ethereumAndStellar val currencies = MockCryptoCurrencyFactory(userWallet).ethereumAndStellar
val actual = finder.find(currencies) val actual = finder.find(currencies)

View file

@ -1,6 +1,7 @@
package com.tangem.domain.card package com.tangem.domain.card
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Blockchain.Companion.fromId
import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Token
import com.tangem.common.card.EllipticCurve import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion import com.tangem.common.card.FirmwareVersion
@ -76,7 +77,7 @@ internal class TangemCardTypesResolver(
} else { } else {
return Blockchain.Unknown return Blockchain.Unknown
} }
Blockchain.Companion.fromBlockchainName(blockchainName) Blockchain.fromBlockchainName(blockchainName)
} }
} }
} }

View file

@ -6,10 +6,7 @@ import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.toMapKey import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.DerivationStyleProvider
import com.tangem.domain.card.TangemCardTypesResolver import com.tangem.domain.card.TangemCardTypesResolver
import com.tangem.domain.card.TangemDerivationStyleProvider
import com.tangem.domain.card.TangemHotDerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.common.TapWorkarounds.isTestCard import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.card.configs.CardConfig
@ -25,18 +22,6 @@ val ScanResponse.cardTypesResolver: CardTypesResolver
walletData = walletData, walletData = walletData,
) )
val UserWallet.derivationStyleProvider: DerivationStyleProvider
get() = when (this) {
is UserWallet.Cold -> this.scanResponse.derivationStyleProvider
is UserWallet.Hot -> TangemHotDerivationStyleProvider()
}
val ScanResponse.derivationStyleProvider: DerivationStyleProvider
get() = card.derivationStyleProvider
val CardDTO.derivationStyleProvider: DerivationStyleProvider
get() = TangemDerivationStyleProvider(this)
val UserWallet.Cold.cardTypesResolver: CardTypesResolver val UserWallet.Cold.cardTypesResolver: CardTypesResolver
get() = scanResponse.cardTypesResolver get() = scanResponse.cardTypesResolver

View file

@ -21,6 +21,7 @@ dependencies {
implementation(projects.domain.staking) implementation(projects.domain.staking)
implementation(projects.domain.tokens) implementation(projects.domain.tokens)
implementation(projects.domain.card) implementation(projects.domain.card)
implementation(projects.domain.wallets)
implementation(projects.domain.legacy) implementation(projects.domain.legacy)
/* Core */ /* Core */

View file

@ -2,7 +2,6 @@ package com.tangem.domain.managetokens
import arrow.core.Either import arrow.core.Either
import arrow.core.flatten import arrow.core.flatten
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.CustomTokensRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
@ -14,6 +13,7 @@ import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
@Suppress("LongParameterList") @Suppress("LongParameterList")
class SaveManagedTokensUseCase( class SaveManagedTokensUseCase(

View file

@ -1,7 +1,7 @@
package com.tangem.domain.markets package com.tangem.domain.markets
import arrow.core.Either import arrow.core.Either
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.markets.repositories.MarketsTokenRepository import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network

View file

@ -25,7 +25,7 @@ class HotUserWalletBuilder @AssistedInject constructor(
) { ) {
suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) { suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) {
val allNetworks = Blockchain.entries val allNetworks = Blockchain.entries // TODO [REDACTED_TASK_KEY] add derivation config
val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet() val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet()
val requests = curves.sortedBy { it.ordinal }.map { curve -> val requests = curves.sortedBy { it.ordinal }.map { curve ->
val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() } val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() }

View file

@ -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
}

View file

@ -1,4 +1,4 @@
package com.tangem.domain.card package com.tangem.domain.wallets.derivations
import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.domain.card.common.TapWorkarounds.isWallet2 import com.tangem.domain.card.common.TapWorkarounds.isWallet2
@ -25,7 +25,6 @@ internal class TangemDerivationStyleProvider(
} }
} }
// TODO remove this class [REDACTED_TASK_KEY]
internal class TangemHotDerivationStyleProvider : DerivationStyleProvider { internal class TangemHotDerivationStyleProvider : DerivationStyleProvider {
override fun getDerivationStyle(): DerivationStyle? = DerivationStyle.V3 override fun getDerivationStyle(): DerivationStyle? = DerivationStyle.V3
} }

View file

@ -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)

View file

@ -1,11 +1,11 @@
package com.tangem.domain.card.repository package com.tangem.domain.wallets.derivations
import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.ByteArrayKey
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.BackendId
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.BackendId
import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.derivation.ExtendedPublicKeysMap
interface DerivationsRepository { interface DerivationsRepository {

View file

@ -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
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.hotwallet package com.tangem.domain.wallets.hot
import com.tangem.hot.sdk.model.HotAuth import com.tangem.hot.sdk.model.HotAuth

View file

@ -1,8 +0,0 @@
package com.tangem.domain.wallets.repository
import com.tangem.domain.models.network.Network
interface HotDerivationsRepository {
fun getAllSupportedNetworks(): Set<Network>
}

View file

@ -1,16 +1,17 @@
package com.tangem.domain.card
package com.tangem.domain.wallets.usecase
import arrow.core.Either import arrow.core.Either
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.derivations.DerivationsRepository
class DerivePublicKeysUseCase( class DerivePublicKeysUseCase(
private val derivationsRepository: DerivationsRepository, private val derivationsRepository: DerivationsRepository,
) { ) {
suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>): Either<Throwable, Unit> { suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>): Either<Throwable, Unit> {
return Either.catch { return Either.Companion.catch {
derivationsRepository.derivePublicKeys(userWalletId, currencies) derivationsRepository.derivePublicKeys(userWalletId, currencies)
} }
} }

View file

@ -1,4 +1,4 @@
package com.tangem.domain.card package com.tangem.domain.wallets.usecase
import arrow.core.Either import arrow.core.Either
import arrow.core.right import arrow.core.right
@ -10,10 +10,10 @@ import com.tangem.common.extensions.calculateSha256
import com.tangem.crypto.NetworkType import com.tangem.crypto.NetworkType
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.derivation.ExtendedPublicKeysMap
/** /**

View file

@ -1,22 +1,20 @@
package com.tangem.domain.card package com.tangem.domain.wallets.usecase
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId 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 * Use case to check if user has missed derivations
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
typealias BackendId = String
class HasMissedDerivationsUseCase( class HasMissedDerivationsUseCase(
private val derivationsRepository: DerivationsRepository, private val derivationsRepository: DerivationsRepository,
) { ) {
/** Check if user [userWalletId] has missed derivations using map of [Network.ID] with extraDerivationPath */ /** Check if user [userWalletId] has missed derivations using map of [com.tangem.domain.models.network.Network.ID] with extraDerivationPath */
suspend operator fun invoke( suspend operator fun invoke(
userWalletId: UserWalletId, userWalletId: UserWalletId,
networksWithDerivationPath: Map<BackendId, String?>, networksWithDerivationPath: Map<BackendId, String?>,

View file

@ -12,6 +12,7 @@ dependencies {
/* Project - Domain */ /* Project - Domain */
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
/* Project - Core */ /* Project - Core */

View file

@ -2,6 +2,7 @@ package com.tangem.features.hotwallet
import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
interface HotAccessCodeRequestComponent : ComposableContentComponent, HotWalletPasswordRequester { interface HotAccessCodeRequestComponent : ComposableContentComponent, HotWalletPasswordRequester {

View file

@ -1,14 +1,13 @@
package com.tangem.features.hotwallet.accesscoderequest package com.tangem.features.hotwallet.accesscoderequest
import androidx.compose.foundation.focusable
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.FullScreen import com.tangem.core.ui.components.FullScreen
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import com.tangem.features.hotwallet.HotAccessCodeRequestComponent import com.tangem.features.hotwallet.HotAccessCodeRequestComponent
import com.tangem.features.hotwallet.HotWalletPasswordRequester
import com.tangem.features.hotwallet.accesscoderequest.ui.HotAccessCodeRequestFullScreenContent import com.tangem.features.hotwallet.accesscoderequest.ui.HotAccessCodeRequestFullScreenContent
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory

View file

@ -2,7 +2,7 @@ package com.tangem.features.hotwallet.accesscoderequest
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.features.hotwallet.HotWalletPasswordRequester import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import com.tangem.features.hotwallet.accesscoderequest.entity.HotAccessCodeRequestUM import com.tangem.features.hotwallet.accesscoderequest.entity.HotAccessCodeRequestUM
import com.tangem.hot.sdk.model.HotAuth import com.tangem.hot.sdk.model.HotAuth
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider

View file

@ -1,8 +1,8 @@
package com.tangem.features.hotwallet.accesscoderequest.di package com.tangem.features.hotwallet.accesscoderequest.di
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import com.tangem.features.hotwallet.HotAccessCodeRequestComponent import com.tangem.features.hotwallet.HotAccessCodeRequestComponent
import com.tangem.features.hotwallet.HotWalletPasswordRequester
import com.tangem.features.hotwallet.accesscoderequest.DefaultHotAccessCodeRequestComponent import com.tangem.features.hotwallet.accesscoderequest.DefaultHotAccessCodeRequestComponent
import com.tangem.features.hotwallet.accesscoderequest.HotAccessCodeRequestModel import com.tangem.features.hotwallet.accesscoderequest.HotAccessCodeRequestModel
import com.tangem.features.hotwallet.accesscoderequest.proxy.HotWalletPasswordRequesterProxy import com.tangem.features.hotwallet.accesscoderequest.proxy.HotWalletPasswordRequesterProxy

View file

@ -1,6 +1,6 @@
package com.tangem.features.hotwallet.accesscoderequest.proxy package com.tangem.features.hotwallet.accesscoderequest.proxy
import com.tangem.features.hotwallet.HotWalletPasswordRequester import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first

View file

@ -30,6 +30,7 @@ dependencies {
implementation(projects.domain.manageTokens) implementation(projects.domain.manageTokens)
implementation(projects.domain.tokens) implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
implementation(projects.domain.swap.models) implementation(projects.domain.swap.models)

View file

@ -9,12 +9,12 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.card.HasMissedDerivationsUseCase
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent
import com.tangem.features.managetokens.component.CustomTokenFormComponent import com.tangem.features.managetokens.component.CustomTokenFormComponent
import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM

View file

@ -17,9 +17,9 @@ import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.card.HasMissedDerivationsUseCase
import com.tangem.domain.managetokens.SaveManagedTokensUseCase import com.tangem.domain.managetokens.SaveManagedTokensUseCase
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent
import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent
import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.component.ManageTokensComponent

View file

@ -13,10 +13,10 @@ import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.card.HasMissedDerivationsUseCase
import com.tangem.domain.managetokens.SaveManagedTokensUseCase import com.tangem.domain.managetokens.SaveManagedTokensUseCase
import com.tangem.domain.redux.OnboardingManageTokensAction import com.tangem.domain.redux.OnboardingManageTokensAction
import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent
import com.tangem.features.managetokens.component.ManageTokensSource import com.tangem.features.managetokens.component.ManageTokensSource
import com.tangem.features.managetokens.component.OnboardingManageTokensComponent import com.tangem.features.managetokens.component.OnboardingManageTokensComponent

View file

@ -15,7 +15,7 @@ import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.SnackbarMessage import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.HasMissedDerivationsUseCase import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase
import com.tangem.domain.managetokens.model.CurrencyUnsupportedState import com.tangem.domain.managetokens.model.CurrencyUnsupportedState
import com.tangem.domain.markets.SaveMarketTokensUseCase import com.tangem.domain.markets.SaveMarketTokensUseCase

View file

@ -4,7 +4,7 @@ import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase

View file

@ -2,7 +2,7 @@ package com.tangem.feature.referral.domain
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase

View file

@ -2,7 +2,7 @@ package com.tangem.feature.referral.domain.di
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.di.ModelComponent
import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.ReferralInteractor import com.tangem.feature.referral.domain.ReferralInteractor

View file

@ -33,7 +33,6 @@ import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.card.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
@ -69,6 +68,7 @@ import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener

View file

@ -9,7 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.card.SetCardWasScannedUseCase import com.tangem.domain.card.SetCardWasScannedUseCase
import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase