Updated on 2026-08-14
This commit is contained in:
parent
88dbdbc7f6
commit
a10c1a8505
24 changed files with 132 additions and 1215 deletions
|
|
@ -5,13 +5,8 @@ import androidx.paging.cachedIn
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
|
|
@ -23,19 +18,14 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.card.DerivePublicKeysUseCase
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.*
|
||||
|
|
@ -66,7 +56,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MaybeTokenListFlow
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -103,7 +92,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
|
|
@ -295,112 +283,30 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onGenerateMissedAddressesClick(missedAddressCurrencies: List<CryptoCurrency>) {
|
||||
val state = uiState as? WalletState.ContentState ?: return
|
||||
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main))
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeScanYourCardTapped)
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
|
||||
|
||||
deriveMissingCurrencies(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
currencyList = missedAddressCurrencies,
|
||||
) { scannedCardResponse ->
|
||||
updateWalletUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
update = { it.copy(scanResponse = scannedCardResponse) },
|
||||
)
|
||||
.onRight {
|
||||
fetchTokenListUseCase(userWalletId = it.walletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: delete [REDACTED_JIRA]
|
||||
// val state = uiState as? WalletState.ContentState ?: return
|
||||
//
|
||||
// analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main))
|
||||
// analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeScanYourCardTapped)
|
||||
//
|
||||
// viewModelScope.launch(dispatchers.io) {
|
||||
// val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
|
||||
//
|
||||
// deriveMissingCurrencies(
|
||||
// scanResponse = userWallet.scanResponse,
|
||||
// currencyList = missedAddressCurrencies,
|
||||
// ) { scannedCardResponse ->
|
||||
// updateWalletUseCase(
|
||||
// userWalletId = userWallet.walletId,
|
||||
// update = { it.copy(scanResponse = scannedCardResponse) },
|
||||
// )
|
||||
// .onRight {
|
||||
// fetchTokenListUseCase(userWalletId = it.walletId)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Deprecated("Use DerivePublicKeysUseCase instead")
|
||||
private fun deriveMissingCurrencies(
|
||||
scanResponse: ScanResponse,
|
||||
currencyList: List<CryptoCurrency>,
|
||||
onSuccess: suspend (ScanResponse) -> Unit,
|
||||
) {
|
||||
val config = CardConfig.createConfig(scanResponse.card)
|
||||
val derivationDataList = currencyList.mapNotNull {
|
||||
config.primaryCurve(blockchain = Blockchain.fromId(it.network.id.value))?.let { curve ->
|
||||
getNewDerivations(curve, scanResponse, it)
|
||||
}
|
||||
}
|
||||
|
||||
val derivations = buildMap<ByteArrayKey, MutableList<DerivationPath>> {
|
||||
derivationDataList.forEach {
|
||||
val current = this[it.derivations.first]
|
||||
if (current != null) {
|
||||
current.addAll(it.derivations.second)
|
||||
current.distinct()
|
||||
} else {
|
||||
this[it.derivations.first] = it.derivations.second.toMutableList()
|
||||
}
|
||||
}
|
||||
}.ifEmpty { return }
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
derivePublicKeysUseCase(cardId = null, derivations = derivations)
|
||||
.onRight {
|
||||
val newDerivedKeys = it.entries
|
||||
val oldDerivedKeys = scanResponse.derivedKeys
|
||||
|
||||
val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet()
|
||||
|
||||
val updatedDerivedKeys = walletKeys.associateWith { walletKey ->
|
||||
val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap())
|
||||
val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
ExtendedPublicKeysMap(oldDerivations + newDerivations)
|
||||
}
|
||||
val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys)
|
||||
|
||||
onSuccess(updatedScanResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNewDerivations(
|
||||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
currency: CryptoCurrency,
|
||||
): DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
val supportedCurves = blockchain.getSupportedCurves()
|
||||
val path = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
|
||||
.takeIf { supportedCurves.contains(curve) }
|
||||
|
||||
val customPath = currency.network.derivationPath.value?.let {
|
||||
DerivationPath(it)
|
||||
}.takeIf { supportedCurves.contains(curve) }
|
||||
|
||||
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
|
||||
if (bothCandidates.isEmpty()) return null
|
||||
|
||||
if (currency is CryptoCurrency.Coin && blockchain == Blockchain.Cardano) {
|
||||
currency.network.derivationPath.value?.let {
|
||||
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
|
||||
}
|
||||
}
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
|
||||
|
||||
val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
if (toDerive.isEmpty()) return null
|
||||
|
||||
return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)
|
||||
}
|
||||
|
||||
class DerivationData(val derivations: Pair<ByteArrayKey, List<DerivationPath>>)
|
||||
|
||||
override fun onScanToUnlockWalletClick() {
|
||||
val state = uiState as? WalletState.ContentState ?: return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
||||
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.card.DerivePublicKeysUseCase
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
|
|
@ -23,7 +14,6 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.wallets.models.UnlockWalletsError
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase
|
||||
import com.tangem.domain.wallets.usecase.UpdateWalletUseCase
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
|
|
@ -35,8 +25,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent
|
|||
import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state2.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender
|
||||
import com.tangem.features.tester.api.TesterFeatureToggles
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -70,7 +58,6 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
private val stateHolder: WalletStateController,
|
||||
private val walletEventSender: WalletEventSender,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val updateWalletUseCase: UpdateWalletUseCase,
|
||||
private val unlockWalletsUseCase: UnlockWalletsUseCase,
|
||||
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
|
||||
private val scanCardToUnlockWalletClickHandler: ScanCardToUnlockWalletClickHandler,
|
||||
|
|
@ -80,7 +67,6 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val testerFeatureToggles: TesterFeatureToggles,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
|
|
@ -117,111 +103,15 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) {
|
||||
derivePublicKeysUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currencies = missedAddressCurrencies,
|
||||
)
|
||||
.onRight { fetchTokenListUseCase(userWalletId = userWallet.walletId) }
|
||||
.onLeft { Timber.e("Failed to derive public keys: $it") }
|
||||
} else {
|
||||
// TODO: delete [REDACTED_JIRA]
|
||||
deriveMissingCurrencies(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
currencyList = missedAddressCurrencies,
|
||||
) { scannedCardResponse ->
|
||||
updateWalletUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
update = { it.copy(scanResponse = scannedCardResponse) },
|
||||
)
|
||||
.onRight { fetchTokenListUseCase(userWalletId = it.walletId) }
|
||||
}
|
||||
}
|
||||
derivePublicKeysUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currencies = missedAddressCurrencies,
|
||||
)
|
||||
.onRight { fetchTokenListUseCase(userWalletId = userWallet.walletId) }
|
||||
.onLeft { Timber.e("Failed to derive public keys: $it") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun deriveMissingCurrencies(
|
||||
scanResponse: ScanResponse,
|
||||
currencyList: List<CryptoCurrency>,
|
||||
onSuccess: suspend (ScanResponse) -> Unit,
|
||||
) {
|
||||
val config = CardConfig.createConfig(scanResponse.card)
|
||||
val derivationDataList = currencyList.mapNotNull {
|
||||
config.primaryCurve(blockchain = Blockchain.fromId(it.network.id.value))?.let { curve ->
|
||||
getNewDerivations(curve, scanResponse, it)
|
||||
}
|
||||
}
|
||||
|
||||
val derivations = buildMap<ByteArrayKey, MutableList<DerivationPath>> {
|
||||
derivationDataList.forEach {
|
||||
val current = this[it.derivations.first]
|
||||
if (current != null) {
|
||||
current.addAll(it.derivations.second)
|
||||
current.distinct()
|
||||
} else {
|
||||
this[it.derivations.first] = it.derivations.second.toMutableList()
|
||||
}
|
||||
}
|
||||
}.ifEmpty { return }
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
derivePublicKeysUseCase(cardId = null, derivations = derivations)
|
||||
.onRight {
|
||||
val newDerivedKeys = it.entries
|
||||
val oldDerivedKeys = scanResponse.derivedKeys
|
||||
|
||||
val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet()
|
||||
|
||||
val updatedDerivedKeys = walletKeys.associateWith { walletKey ->
|
||||
val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap())
|
||||
val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
ExtendedPublicKeysMap(oldDerivations + newDerivations)
|
||||
}
|
||||
val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys)
|
||||
|
||||
onSuccess(updatedScanResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNewDerivations(
|
||||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
currency: CryptoCurrency,
|
||||
): DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
val supportedCurves = blockchain.getSupportedCurves()
|
||||
val path = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
|
||||
.takeIf { supportedCurves.contains(curve) }
|
||||
|
||||
val customPath = currency.network.derivationPath.value?.let {
|
||||
DerivationPath(it)
|
||||
}.takeIf { supportedCurves.contains(curve) }
|
||||
|
||||
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
|
||||
if (bothCandidates.isEmpty()) return null
|
||||
|
||||
if (currency is CryptoCurrency.Coin && blockchain == Blockchain.Cardano) {
|
||||
currency.network.derivationPath.value?.let {
|
||||
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
|
||||
}
|
||||
}
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
|
||||
|
||||
val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
if (toDerive.isEmpty()) return null
|
||||
|
||||
return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)
|
||||
}
|
||||
|
||||
class DerivationData(val derivations: Pair<ByteArrayKey, List<DerivationPath>>)
|
||||
|
||||
override fun onOpenUnlockWalletsBottomSheetClick() {
|
||||
val config = requireNotNull(stateHolder.getSelectedWallet().bottomSheetConfig) {
|
||||
"Impossible to open unlock wallet bottom sheet if it's null"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue