Updated on 2026-08-14
This commit is contained in:
parent
75e2268d10
commit
df9ba4e5f1
11 changed files with 136 additions and 251 deletions
|
|
@ -25,6 +25,7 @@ import com.tangem.datasource.connection.NetworkConnectionManager
|
|||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletManagersRepository
|
||||
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||
|
|
@ -169,6 +170,9 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
@Inject
|
||||
lateinit var walletManagersFacade: WalletManagersFacade
|
||||
|
||||
@Inject
|
||||
lateinit var currenciesRepository: CurrenciesRepository
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
|
@ -190,6 +194,7 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
appCurrencyRepository = appCurrencyRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
appStateHolder = appStateHolder,
|
||||
currenciesRepository = currenciesRepository,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -116,4 +116,10 @@ internal object TokensDomainModule {
|
|||
): GetCryptoCurrencyActionsUseCase {
|
||||
return GetCryptoCurrencyActionsUseCase(rampStateManager, marketCryptoCurrencyRepository, dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
|
||||
return GetCryptoCurrenciesUseCase(currenciesRepository = currenciesRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,6 @@ internal object TokensListInteractorModule {
|
|||
reduxStateHolder = reduxStateHolder,
|
||||
testnetTokensStorage = testnetTokensStorage,
|
||||
),
|
||||
reduxStateHolder = reduxStateHolder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,252 +1,17 @@
|
|||
package com.tangem.tap.features.tokens.impl.domain
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.common.util.supportsHdWallet
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.features.tokens.impl.domain.models.Token
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokenWithBlockchain
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Default implementation of tokens list interactor
|
||||
* FIXME("Necessary to avoid using redux actions")
|
||||
*
|
||||
* @property repository repository of tokens list feature
|
||||
* @property reduxStateHolder redux state holder
|
||||
* @property repository repository of tokens list feature
|
||||
*/
|
||||
internal class DefaultTokensListInteractor(
|
||||
private val repository: TokensListRepository,
|
||||
private val reduxStateHolder: AppStateHolder,
|
||||
) : TokensListInteractor {
|
||||
internal class DefaultTokensListInteractor(private val repository: TokensListRepository) : TokensListInteractor {
|
||||
|
||||
override fun getTokensList(searchText: String): Flow<PagingData<Token>> {
|
||||
return repository.getAvailableTokens(searchText = searchText.ifBlank(defaultValue = { null }))
|
||||
}
|
||||
|
||||
override suspend fun saveChanges(tokens: List<TokenWithBlockchain>, blockchains: List<Blockchain>) {
|
||||
val scanResponse = requireNotNull(reduxStateHolder.scanResponse)
|
||||
|
||||
val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle()
|
||||
val currentTokens = store.state.tokensState.addedWallets
|
||||
.toNonCustomTokensWithBlockchains(derivationStyle = derivationStyle)
|
||||
|
||||
val currentBlockchains = store.state.tokensState.addedWallets
|
||||
.toNonCustomBlockchains(derivationStyle = derivationStyle)
|
||||
|
||||
val blockchainsToAdd = blockchains.filterNot(currentBlockchains::contains)
|
||||
val blockchainsToRemove = currentBlockchains.filterNot(blockchains::contains)
|
||||
|
||||
val tokensToAdd = tokens.filterNot(currentTokens::contains)
|
||||
val tokensToRemove = currentTokens.filterNot { token -> tokens.any { it.token == token.token } }
|
||||
|
||||
val isNothingToDoWithTokens = tokensToAdd.isEmpty() && tokensToRemove.isEmpty()
|
||||
val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty()
|
||||
if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) {
|
||||
store.dispatchDebugErrorNotification(message = "Nothing to save")
|
||||
return
|
||||
}
|
||||
|
||||
remove(
|
||||
tokens = tokensToRemove,
|
||||
blockchains = blockchainsToRemove,
|
||||
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
)
|
||||
|
||||
add(tokens = tokensToAdd, blockchains = blockchainsToAdd, scanResponse = scanResponse)
|
||||
}
|
||||
|
||||
private fun List<WalletDataModel>.toNonCustomTokensWithBlockchains(
|
||||
derivationStyle: DerivationStyle?,
|
||||
): List<TokenWithBlockchain> {
|
||||
return this.map(WalletDataModel::currency)
|
||||
.mapNotNull { currency ->
|
||||
if (currency !is Currency.Token || currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
|
||||
TokenWithBlockchain(token = currency.token, blockchain = currency.blockchain)
|
||||
}
|
||||
.distinct()
|
||||
}
|
||||
|
||||
private fun List<WalletDataModel>.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List<Blockchain> {
|
||||
return this.map(WalletDataModel::currency)
|
||||
.mapNotNull { currency ->
|
||||
if (currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
|
||||
(currency as? Currency.Blockchain)?.blockchain
|
||||
}
|
||||
.distinct()
|
||||
}
|
||||
|
||||
private suspend fun remove(
|
||||
tokens: List<TokenWithBlockchain>,
|
||||
blockchains: List<Blockchain>,
|
||||
derivationStyle: DerivationStyle?,
|
||||
) {
|
||||
val currencies = convertToCurrencies(tokens, blockchains, derivationStyle)
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
Timber.e("Unable to remove currencies, no user wallet selected")
|
||||
return
|
||||
}
|
||||
|
||||
walletCurrenciesManager.removeCurrencies(userWallet = selectedUserWallet, currenciesToRemove = currencies)
|
||||
}
|
||||
|
||||
private suspend fun add(
|
||||
tokens: List<TokenWithBlockchain>,
|
||||
blockchains: List<Blockchain>,
|
||||
scanResponse: ScanResponse,
|
||||
) {
|
||||
val currenciesToAdd = convertToCurrencies(
|
||||
tokens = tokens,
|
||||
blockchains = blockchains,
|
||||
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
)
|
||||
|
||||
// TODO("[REDACTED_TASK_KEY] use DerivationManager")
|
||||
if (scanResponse.supportsHdWallet()) {
|
||||
deriveMissingBlockchains(scanResponse, currenciesToAdd)
|
||||
} else {
|
||||
submitAdd(scanResponse, currenciesToAdd)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun deriveMissingBlockchains(scanResponse: ScanResponse, currencies: List<Currency>) {
|
||||
val config = CardConfig.createConfig(scanResponse.card)
|
||||
val derivations = currencies.mapNotNull {
|
||||
val curve = config.primaryCurve(it.blockchain)
|
||||
curve?.let { getDerivations(curve, scanResponse, currencies) }
|
||||
}.associate(transform = TokensMiddleware.DerivationData::derivations)
|
||||
|
||||
if (derivations.isEmpty()) {
|
||||
submitAdd(scanResponse, currencies)
|
||||
return
|
||||
}
|
||||
|
||||
when (val result = tangemSdkManager.derivePublicKeys(cardId = null, derivations = derivations)) {
|
||||
is CompletionResult.Success -> {
|
||||
val newDerivedKeys = result.data.entries
|
||||
val oldDerivedKeys = scanResponse.derivedKeys
|
||||
|
||||
val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet()
|
||||
|
||||
val updatedDerivedKeys = walletKeys.associateWith { walletKey ->
|
||||
val oldDerivations = ExtendedPublicKeysMap(map = oldDerivedKeys[walletKey] ?: emptyMap())
|
||||
val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(map = emptyMap())
|
||||
ExtendedPublicKeysMap(map = oldDerivations + newDerivations)
|
||||
}
|
||||
|
||||
val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys)
|
||||
|
||||
store.dispatchOnMain(GlobalAction.SaveScanResponse(updatedScanResponse))
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
|
||||
submitAdd(scanResponse, currencies)
|
||||
return
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
store.dispatchDebugErrorNotification(TapError.CustomError(customMessage = "Error adding tokens"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDerivations(
|
||||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
currencyList: List<Currency>,
|
||||
): TokensMiddleware.DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val manageTokensCandidates = currencyList
|
||||
.map(Currency::blockchain)
|
||||
.distinct()
|
||||
.filter { it.getSupportedCurves().contains(curve) }
|
||||
.mapNotNull { it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) }
|
||||
|
||||
val customTokensCandidates = currencyList
|
||||
.filter { it.blockchain.getSupportedCurves().contains(curve) }
|
||||
.mapNotNull(Currency::derivationPath)
|
||||
.map(::DerivationPath)
|
||||
|
||||
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
|
||||
if (bothCandidates.isEmpty()) return null
|
||||
|
||||
currencyList.find { it is Currency.Blockchain && it.blockchain == Blockchain.Cardano }?.let { currency ->
|
||||
currency.derivationPath?.let {
|
||||
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
|
||||
}
|
||||
}
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys = scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
|
||||
|
||||
val toDerive = bothCandidates.filterNot(alreadyDerivedPaths::contains)
|
||||
if (toDerive.isEmpty()) return null
|
||||
|
||||
return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)
|
||||
}
|
||||
|
||||
private suspend fun submitAdd(scanResponse: ScanResponse, currencies: List<Currency>) {
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
Timber.e("Unable to add currencies, no user wallet selected")
|
||||
return
|
||||
}
|
||||
|
||||
userWalletsListManager
|
||||
.update(
|
||||
userWalletId = selectedUserWallet.walletId,
|
||||
update = { userWallet -> userWallet.copy(scanResponse = scanResponse) },
|
||||
)
|
||||
.flatMap { updatedUserWallet ->
|
||||
walletCurrenciesManager.addCurrencies(
|
||||
userWallet = updatedUserWallet,
|
||||
currenciesToAdd = currencies,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertToCurrencies(
|
||||
tokens: List<TokenWithBlockchain>,
|
||||
blockchains: List<Blockchain>,
|
||||
derivationStyle: DerivationStyle?,
|
||||
): List<Currency> {
|
||||
return tokens.map { tokenWithBlockchain ->
|
||||
Currency.Token(
|
||||
token = tokenWithBlockchain.token,
|
||||
blockchain = tokenWithBlockchain.blockchain,
|
||||
derivationPath = tokenWithBlockchain.blockchain.derivationPath(derivationStyle)?.rawPath,
|
||||
)
|
||||
}.plus(
|
||||
blockchains.map { blockchain ->
|
||||
Currency.Blockchain(
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.tap.features.tokens.impl.domain
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.features.tokens.impl.domain.models.Token
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokenWithBlockchain
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
@ -15,12 +13,4 @@ internal interface TokensListInteractor {
|
|||
|
||||
/** Get tokens list using filter by text [searchText] */
|
||||
fun getTokensList(searchText: String): Flow<PagingData<Token>>
|
||||
|
||||
/**
|
||||
* Save added tokens
|
||||
*
|
||||
* @param tokens tokens list that need to save
|
||||
* @param blockchains blockchains list that need to save
|
||||
*/
|
||||
suspend fun saveChanges(tokens: List<TokenWithBlockchain>, blockchains: List<Blockchain>)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
|||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
|
|
@ -37,6 +38,9 @@ data class DaggerGraphState(
|
|||
val appCurrencyRepository: AppCurrencyRepository? = null,
|
||||
val walletManagersFacade: WalletManagersFacade? = null,
|
||||
val appStateHolder: AppStateHolder? = null,
|
||||
|
||||
// FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList
|
||||
val currenciesRepository: CurrenciesRepository? = null,
|
||||
) : StateType {
|
||||
|
||||
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue