Updated on 2026-08-14
This commit is contained in:
commit
ae15bcfc55
211 changed files with 4715 additions and 3641 deletions
|
|
@ -49,7 +49,9 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -161,10 +163,6 @@ internal class DefaultStakeKitRepository(
|
|||
|
||||
private fun getAvailableStakeKitIntegrationsIds(): List<StakingIntegrationID.StakeKit> {
|
||||
return StakingIntegrationID.StakeKit.entries
|
||||
// load all integrations for now and filter in use cases if needed
|
||||
// .filterNot {
|
||||
// it.blockchain == Blockchain.Cardano && !stakingFeatureToggles.isCardanoStakingEnabled
|
||||
// }
|
||||
}
|
||||
|
||||
private fun NetworkTypeDTO.extractJsonName(): String {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.data.staking.store.StakeKitBalancesStore
|
|||
import com.tangem.domain.card.common.TapWorkarounds.isWallet2
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
|
|
@ -16,7 +15,6 @@ import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
|||
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isCardano
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||
|
|
@ -34,7 +32,6 @@ internal class DefaultStakingRepository(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
) : StakingRepository {
|
||||
|
||||
override fun getStakingAvailability(
|
||||
|
|
@ -42,7 +39,7 @@ internal class DefaultStakingRepository(
|
|||
cryptoCurrency: CryptoCurrency,
|
||||
): Flow<StakingAvailability> {
|
||||
return channelFlow {
|
||||
if (!checkFeatureToggleEnabled(userWalletId, cryptoCurrency)) {
|
||||
if (!checkFeatureToggleEnabled(cryptoCurrency)) {
|
||||
send(StakingAvailability.Unavailable)
|
||||
return@channelFlow
|
||||
}
|
||||
|
|
@ -78,7 +75,7 @@ internal class DefaultStakingRepository(
|
|||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): StakingAvailability {
|
||||
if (!checkFeatureToggleEnabled(userWalletId, cryptoCurrency)) {
|
||||
if (!checkFeatureToggleEnabled(cryptoCurrency)) {
|
||||
return StakingAvailability.Unavailable
|
||||
}
|
||||
|
||||
|
|
@ -118,31 +115,14 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun checkFeatureToggleEnabled(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
private fun checkFeatureToggleEnabled(cryptoCurrency: CryptoCurrency): Boolean {
|
||||
return when (cryptoCurrency.network.id.toBlockchain()) {
|
||||
Blockchain.TON -> stakingFeatureToggles.isTonStakingEnabled
|
||||
Blockchain.Ethereum -> {
|
||||
when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> stakingFeatureToggles.isEthStakingEnabled
|
||||
is CryptoCurrency.Token -> true
|
||||
}
|
||||
}
|
||||
Blockchain.Cardano -> {
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
|
||||
val balance = stakingBalanceStoreV2.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
stakingId = StakingID(
|
||||
integrationId = StakingIntegrationID.create(currencyId = cryptoCurrency.id)?.value
|
||||
?: return false,
|
||||
address = address,
|
||||
),
|
||||
)
|
||||
if ((balance as? StakingBalance.Data.StakeKit)?.balance?.items?.isNotEmpty() == true) {
|
||||
return true
|
||||
} else {
|
||||
stakingFeatureToggles.isCardanoStakingEnabled
|
||||
}
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ internal object StakingDataModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
getUserWalletUseCase: GetUserWalletUseCase,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
|
|
@ -69,7 +68,6 @@ internal object StakingDataModule {
|
|||
stakingBalanceStoreV2 = stakeKitBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,6 @@ internal class DefaultStakingFeatureToggles(
|
|||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : StakingFeatureToggles {
|
||||
|
||||
override val isTonStakingEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "STAKING_TON_ENABLED")
|
||||
|
||||
override val isCardanoStakingEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("STAKING_CARDANO_ENABLED")
|
||||
|
||||
override val isEthStakingEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("STAKING_ETH_ENABLED")
|
||||
}
|
||||
|
|
@ -41,8 +41,6 @@ dependencies {
|
|||
/** Feature API - remove after removing [HotWalletFeatureToggles] */
|
||||
implementation(projects.features.hotWallet.api)
|
||||
|
||||
/** Feature API - remove after removing [TangemPayFeatureToggles] */
|
||||
implementation(projects.features.tangempay.details.api)
|
||||
|
||||
/** Project - Utils */
|
||||
implementation(projects.core.utils)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package com.tangem.data.wallets.derivations
|
||||
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.card.common.TapWorkarounds.hasOldStyleDerivation
|
||||
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.config.ColdCurvesConfig
|
||||
import com.tangem.domain.wallets.config.CurvesConfig
|
||||
import com.tangem.domain.wallets.config.curvesConfig
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
/**
|
||||
* Source of derivations data
|
||||
*/
|
||||
internal sealed interface DerivationsSource {
|
||||
|
||||
val isHDWalletAllowed: Boolean
|
||||
val hasOldStyleDerivation: Boolean
|
||||
val curvesConfig: CurvesConfig
|
||||
val derivationStyleProvider: DerivationStyleProvider
|
||||
|
||||
fun getWalletPublicKey(curve: EllipticCurve): ByteArray?
|
||||
fun getDerivedKeys(publicKey: KeyWalletPublicKey): ExtendedPublicKeysMap
|
||||
|
||||
data class FromUserWallet(val userWallet: UserWallet) : DerivationsSource {
|
||||
override val isHDWalletAllowed: Boolean
|
||||
get() = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.settings.isHDWalletAllowed
|
||||
is UserWallet.Hot -> true
|
||||
}
|
||||
|
||||
override val hasOldStyleDerivation: Boolean
|
||||
get() = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.hasOldStyleDerivation
|
||||
is UserWallet.Hot -> false
|
||||
}
|
||||
|
||||
override val curvesConfig: CurvesConfig
|
||||
get() = userWallet.curvesConfig
|
||||
|
||||
override val derivationStyleProvider: DerivationStyleProvider
|
||||
get() = userWallet.derivationStyleProvider
|
||||
|
||||
override fun getWalletPublicKey(curve: EllipticCurve): ByteArray? {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.getWalletPublicKey(curve)
|
||||
is UserWallet.Hot -> userWallet.wallets
|
||||
?.firstOrNull { it.curve == curve && it.chainCode != null }
|
||||
?.publicKey
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDerivedKeys(publicKey: KeyWalletPublicKey): ExtendedPublicKeysMap {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.getDerivedKeys(publicKey)
|
||||
is UserWallet.Hot -> {
|
||||
val derivedKeys = userWallet.wallets
|
||||
?.firstOrNull { it.publicKey.contentEquals(publicKey.bytes) }
|
||||
?.derivedKeys
|
||||
.orEmpty()
|
||||
|
||||
ExtendedPublicKeysMap(derivedKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class FromScanResponse(val scanResponse: ScanResponse) : DerivationsSource {
|
||||
override val isHDWalletAllowed: Boolean
|
||||
get() = scanResponse.card.settings.isHDWalletAllowed
|
||||
|
||||
override val hasOldStyleDerivation: Boolean
|
||||
get() = scanResponse.card.hasOldStyleDerivation
|
||||
|
||||
override val curvesConfig: CurvesConfig
|
||||
get() = ColdCurvesConfig(scanResponse.card)
|
||||
|
||||
override val derivationStyleProvider: DerivationStyleProvider
|
||||
get() = scanResponse.derivationStyleProvider
|
||||
|
||||
override fun getWalletPublicKey(curve: EllipticCurve): ByteArray? {
|
||||
return scanResponse.getWalletPublicKey(curve)
|
||||
}
|
||||
|
||||
override fun getDerivedKeys(publicKey: KeyWalletPublicKey): ExtendedPublicKeysMap {
|
||||
return scanResponse.getDerivedKeys(publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ScanResponse.getWalletPublicKey(curve: EllipticCurve): ByteArray? {
|
||||
return card.wallets.firstOrNull { it.curve == curve && it.chainCode != null }
|
||||
?.publicKey
|
||||
}
|
||||
|
||||
private fun ScanResponse.getDerivedKeys(publicKey: KeyWalletPublicKey): ExtendedPublicKeysMap {
|
||||
return derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
}
|
||||
|
|
@ -3,45 +3,80 @@ package com.tangem.data.wallets.derivations
|
|||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
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.scan.KeyWalletPublicKey
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.config.curvesConfig
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import kotlin.collections.forEach
|
||||
|
||||
private typealias DerivationData = Pair<ByteArrayKey, List<DerivationPath>>
|
||||
internal typealias Derivations = Map<ByteArrayKey, List<DerivationPath>>
|
||||
|
||||
/**
|
||||
* Data class representing a blockchain with its derivation path
|
||||
*/
|
||||
data class BlockchainToDerive(
|
||||
val blockchain: Blockchain,
|
||||
val derivationPath: DerivationPath,
|
||||
)
|
||||
|
||||
/**
|
||||
* Finder of missed derivations
|
||||
*
|
||||
* @property userWallet User wallet to find derivations for
|
||||
* @property source Source of derivations data (UserWallet or ScanResponse)
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class MissedDerivationsFinder(private val userWallet: UserWallet) {
|
||||
class MissedDerivationsFinder private constructor(private val source: DerivationsSource) {
|
||||
|
||||
/**
|
||||
* Secondary constructor for backward compatibility with UserWallet
|
||||
*/
|
||||
constructor(userWallet: UserWallet) : this(DerivationsSource.FromUserWallet(userWallet))
|
||||
|
||||
/**
|
||||
* Secondary constructor for ScanResponse
|
||||
*/
|
||||
constructor(scanResponse: ScanResponse) : this(DerivationsSource.FromScanResponse(scanResponse))
|
||||
|
||||
/** Find missed derivations for given currencies [currencies] */
|
||||
fun find(currencies: List<CryptoCurrency>): Derivations {
|
||||
return currencies.map { it.network }.let(::findByNetworks)
|
||||
}
|
||||
|
||||
/** Find missed derivations for given [Network] list */
|
||||
fun findByNetworks(networks: List<Network>): Derivations {
|
||||
val blockchainsToDerive = networks.mapNotNull { network ->
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value?.let(::DerivationPath)
|
||||
?: return@mapNotNull null
|
||||
|
||||
BlockchainToDerive(blockchain, derivationPath)
|
||||
}
|
||||
return findByBlockchainsToDerive(blockchainsToDerive)
|
||||
}
|
||||
|
||||
/** Find missed derivations for given [BlockchainToDerive] list */
|
||||
fun findByBlockchainsToDerive(blockchainsToDerive: Collection<BlockchainToDerive>): Derivations {
|
||||
val enrichedBlockchains = blockchainsToDerive.enrichBlockchains()
|
||||
return findDerivationsInternal(enrichedBlockchains)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common implementation for finding derivations
|
||||
*/
|
||||
private fun findDerivationsInternal(items: Collection<BlockchainToDerive>): Derivations {
|
||||
return buildMap<ByteArrayKey, MutableList<DerivationPath>> {
|
||||
networks
|
||||
.mapToNewDerivations()
|
||||
items
|
||||
.mapNotNull(::mapToNewDerivation)
|
||||
.forEach { data ->
|
||||
val current = this[data.first]
|
||||
if (current != null) {
|
||||
current.addAll(data.second)
|
||||
current.distinct()
|
||||
this[data.first] = current.distinct().toMutableList()
|
||||
} else {
|
||||
this[data.first] = data.second.toMutableList()
|
||||
}
|
||||
|
|
@ -49,31 +84,17 @@ internal class MissedDerivationsFinder(private val userWallet: UserWallet) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun List<Network>.mapToNewDerivations(): List<DerivationData> {
|
||||
return mapNotNull { network ->
|
||||
val blockchain = network.toBlockchain()
|
||||
val curve = userWallet.curvesConfig.primaryCurve(blockchain) ?: return@mapNotNull null
|
||||
/**
|
||||
* Maps a single BlockchainToDerive to derivation data (public key -> derivation paths)
|
||||
*/
|
||||
private fun mapToNewDerivation(input: BlockchainToDerive): DerivationData? {
|
||||
val curve = source.curvesConfig.primaryCurve(input.blockchain) ?: return null
|
||||
if (!input.blockchain.getSupportedCurves().contains(curve)) return null
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
val publicKey = source.getWalletPublicKey(curve) ?: return null
|
||||
|
||||
walletPublicKey?.let {
|
||||
findNewDerivations(curve = curve, publicKey = it, network = network)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findNewDerivations(curve: EllipticCurve, publicKey: ByteArray, network: Network): DerivationData? {
|
||||
val derivationCandidates = network
|
||||
.getDerivationCandidates(curve)
|
||||
val derivationCandidates = input.blockchain
|
||||
.getDerivationCandidates(input.derivationPath)
|
||||
.ifEmpty { return null }
|
||||
.filterAlreadyDerivedKeys(publicKey.toMapKey())
|
||||
.ifEmpty { return null }
|
||||
|
|
@ -81,59 +102,63 @@ internal class MissedDerivationsFinder(private val userWallet: UserWallet) {
|
|||
return publicKey.toMapKey() to derivationCandidates
|
||||
}
|
||||
|
||||
private fun Network.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> {
|
||||
val blockchain = this.toBlockchain()
|
||||
|
||||
/**
|
||||
* Gets all possible derivation paths for a blockchain
|
||||
*/
|
||||
private fun Blockchain.getDerivationCandidates(derivationPath: DerivationPath): List<DerivationPath> {
|
||||
return buildList {
|
||||
add(blockchain.getDerivationPath(curve = curve))
|
||||
add(blockchain.getCustomDerivationPath(curve = curve, network = this@getDerivationCandidates))
|
||||
add(blockchain.getCardanoDerivationPathIfNeeded(network = this@getDerivationCandidates))
|
||||
// Default derivation path for blockchain
|
||||
add(getDerivationPath())
|
||||
|
||||
// The specified derivation path (can be either default or custom)
|
||||
add(derivationPath)
|
||||
|
||||
// Extended Cardano derivation path if needed
|
||||
add(getCardanoExtendedDerivationPath(derivationPath))
|
||||
}
|
||||
.filterNotNull()
|
||||
.distinct()
|
||||
}
|
||||
|
||||
private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? {
|
||||
return if (getSupportedCurves().contains(curve)) {
|
||||
derivationPath(style = userWallet.derivationStyleProvider.getDerivationStyle())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private fun Blockchain.getDerivationPath(): DerivationPath? {
|
||||
return derivationPath(style = source.derivationStyleProvider.getDerivationStyle())
|
||||
}
|
||||
|
||||
private fun Blockchain.getCustomDerivationPath(curve: EllipticCurve, network: Network): DerivationPath? {
|
||||
return if (getSupportedCurves().contains(curve)) {
|
||||
network.derivationPath.value?.let(::DerivationPath)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Blockchain.getCardanoDerivationPathIfNeeded(network: Network): DerivationPath? {
|
||||
return if (this == Blockchain.Cardano) {
|
||||
network.derivationPath.value?.let {
|
||||
CardanoUtils.extendedDerivationPath(derivationPath = DerivationPath(it))
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private fun Blockchain.getCardanoExtendedDerivationPath(customDerivationPath: DerivationPath): DerivationPath? {
|
||||
if (this != Blockchain.Cardano) return null
|
||||
return CardanoUtils.extendedDerivationPath(derivationPath = customDerivationPath)
|
||||
}
|
||||
|
||||
private fun List<DerivationPath>.filterAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> {
|
||||
val alreadyDerivedPaths = getAlreadyDerivedKeys(publicKey)
|
||||
val alreadyDerivedPaths = source.getDerivedKeys(publicKey).keys.toList()
|
||||
return filterNot(alreadyDerivedPaths::contains)
|
||||
}
|
||||
|
||||
private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> {
|
||||
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())
|
||||
}
|
||||
// region Blockchain enrichment logic
|
||||
|
||||
/**
|
||||
* Enriches blockchains collection:
|
||||
* - Adds Ethereum if HD wallet is allowed
|
||||
* - Removes unnecessary blockchains that share derivation path with Ethereum (for cards without old style derivation)
|
||||
*/
|
||||
private fun Collection<BlockchainToDerive>.enrichBlockchains(): Collection<BlockchainToDerive> {
|
||||
if (!source.isHDWalletAllowed) return this
|
||||
|
||||
val derivationStyle = source.derivationStyleProvider.getDerivationStyle()
|
||||
val ethereumDerivationPath = Blockchain.Ethereum.derivationPath(derivationStyle) ?: return this
|
||||
|
||||
val withEthereum = this + BlockchainToDerive(Blockchain.Ethereum, ethereumDerivationPath)
|
||||
|
||||
// For cards with old style derivation, keep all blockchains
|
||||
if (source.hasOldStyleDerivation) {
|
||||
return withEthereum.distinct()
|
||||
}
|
||||
|
||||
return extendedPublicKeysMap.keys.toList()
|
||||
// For new cards: filter out blockchains with same derivation path as Ethereum (except Ethereum itself)
|
||||
return withEthereum
|
||||
.filter { it.derivationPath != ethereumDerivationPath || it.blockchain == Blockchain.Ethereum }
|
||||
.distinct()
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -47,18 +47,11 @@ class DefaultHotWalletAccessCodeAttemptsRepository @Inject constructor(
|
|||
hotWalletId = hotWalletId,
|
||||
auth = true,
|
||||
)
|
||||
val noAuthAttemptId = HotWalletAccessCodeAttemptsRepository.AttemptId(
|
||||
hotWalletId = hotWalletId,
|
||||
auth = false,
|
||||
)
|
||||
|
||||
appPreferencesStore.editData {
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockAttemptsKey(authAttemptId.attemptIdKey()))
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockAttemptsKey(noAuthAttemptId.attemptIdKey()))
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockBootKey(authAttemptId.attemptIdKey()))
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockBootKey(noAuthAttemptId.attemptIdKey()))
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockDeadlineKey(authAttemptId.attemptIdKey()))
|
||||
it.remove(PreferencesKeys.getHotWalletUnlockDeadlineKey(noAuthAttemptId.attemptIdKey()))
|
||||
appPreferencesStore.editData { data ->
|
||||
data.remove(PreferencesKeys.getHotWalletUnlockAttemptsKey(authAttemptId.attemptIdKey()))
|
||||
data.remove(PreferencesKeys.getHotWalletUnlockBootKey(authAttemptId.attemptIdKey()))
|
||||
data.remove(PreferencesKeys.getHotWalletUnlockDeadlineKey(authAttemptId.attemptIdKey()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,13 +112,21 @@ class DefaultHotWalletAccessCodeAttemptsRepository @Inject constructor(
|
|||
}
|
||||
else -> {
|
||||
val remaining = remainingSeconds(deadlineElapsed, bootStored)
|
||||
Attempts.WithDelay(count, remaining)
|
||||
val newCount = if (id.auth) {
|
||||
count
|
||||
} else {
|
||||
MAX_FAST_FORWARD_ATTEMPTS
|
||||
}
|
||||
Attempts.WithDelay(newCount, remaining)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun HotWalletAccessCodeAttemptsRepository.AttemptId.attemptIdKey(): String {
|
||||
return "${hotWalletId.value}_$auth"
|
||||
// Regarding [REDACTED_TASK_KEY], the attempts counter must be shared between modes (auth vs signing).
|
||||
// To provide backward compatibility, we use the same keys but read attempts in auth mode for security reasons.
|
||||
val isAuthMode = true
|
||||
return "${hotWalletId.value}_$isAuthMode"
|
||||
}
|
||||
|
||||
private fun currentBootCount(): Int = Settings.Global.getInt(context.contentResolver, Settings.Global.BOOT_COUNT, 0)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ import com.tangem.domain.card.configs.GenericCardConfig
|
|||
import com.tangem.domain.card.configs.MultiWalletCardConfig
|
||||
import com.tangem.domain.card.configs.Wallet2CardConfig
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class MissedDerivationsFinderTest {
|
||||
|
||||
@Test
|
||||
|
|
@ -97,9 +99,8 @@ internal class MissedDerivationsFinderTest {
|
|||
val currencies = MockCryptoCurrencyFactory(userWallet).cardano.let(::listOf)
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
ByteArrayKey(EllipticCurve.Ed25519.name.toByteArray()),
|
||||
listOf(
|
||||
val expected = mapOf(
|
||||
ByteArrayKey(EllipticCurve.Ed25519.name.toByteArray()) to listOf(
|
||||
DerivationConfigV2.derivations(Blockchain.Cardano).values.first(),
|
||||
CardanoUtils.extendedDerivationPath(
|
||||
derivationPath = DerivationPath(
|
||||
|
|
@ -108,7 +109,12 @@ internal class MissedDerivationsFinderTest {
|
|||
),
|
||||
),
|
||||
),
|
||||
ByteArrayKey(EllipticCurve.Secp256k1.name.toByteArray()) to listOf(
|
||||
DerivationConfigV2.derivations(Blockchain.Ethereum).values.first(),
|
||||
),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual).containsExactlyEntriesIn(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue