Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-09 13:28:33 +05:00
parent a51012484f
commit 32ced9df85
12 changed files with 87 additions and 33 deletions

View file

@ -186,6 +186,7 @@ internal class DefaultCurrenciesRepository(
override suspend fun getMultiCurrencyWalletCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency = withContext(dispatchers.io) {
val userWallet = getUserWallet(userWalletId)
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
@ -194,10 +195,14 @@ internal class DefaultCurrenciesRepository(
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
}
responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse)
responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse, derivationPath.value)
}
override suspend fun getNetworkCoin(userWalletId: UserWalletId, networkId: Network.ID): CryptoCurrency.Coin {
override suspend fun getNetworkCoin(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency.Coin {
val userWallet = getUserWallet(userWalletId)
ensureIsCorrectUserWallet(userWallet = userWallet, isMultiCurrencyWalletExpected = true)
@ -207,15 +212,12 @@ internal class DefaultCurrenciesRepository(
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
}
val blockchain = Blockchain.fromId(networkId.value)
val derivationPath = blockchain
.derivationPath(userWallet.scanResponse.derivationStyleProvider.getDerivationStyle())
?.rawPath
val blockchainNetworkId = blockchain.toNetworkId()
val coinId = blockchain.toCoinId()
val storedCoin = storedTokens.tokens
.find {
it.networkId == blockchain.toNetworkId() &&
it.id == blockchain.toCoinId() &&
it.derivationPath == derivationPath
it.networkId == blockchainNetworkId && it.id == coinId && it.derivationPath == derivationPath.value
} ?: error("Coin in this network $networkId not found")
val coin = responseCurrenciesFactory.createCurrency(storedCoin, userWallet.scanResponse)

View file

@ -20,13 +20,10 @@ internal class ResponseCryptoCurrenciesFactory(private val demoConfig: DemoConfi
currencyId: CryptoCurrency.ID,
response: UserTokensResponse,
scanResponse: ScanResponse,
derivationPath: String?,
): CryptoCurrency {
val responseTokenId = currencyId.rawCurrencyId
val blockchain = Blockchain.fromId(currencyId.rawNetworkId)
val networkId = blockchain.toNetworkId()
val derivationPath = blockchain
.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
?.rawPath
val networkId = Blockchain.fromId(currencyId.rawNetworkId).toNetworkId()
val token = requireNotNull(
value = response.tokens

View file

@ -36,16 +36,18 @@ class FetchCurrencyStatusUseCase(
*
* @param userWalletId The ID of the user's wallet.
* @param id The ID of the cryptocurrency.
* @param derivationPath currency derivation path.
* @param refresh Indicates whether to force a refresh of the status data.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
refresh: Boolean = false,
): Either<CurrencyStatusError, Unit> {
return either {
val currency = getCurrency(userWalletId, id)
val currency = getCurrency(userWalletId, id, derivationPath)
fetchCurrencyStatus(userWalletId, currency, refresh)
}
@ -87,8 +89,9 @@ class FetchCurrencyStatusUseCase(
private suspend fun Raise<CurrencyStatusError>.getCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency {
return catch({ currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, id) }) {
return catch({ currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, id, derivationPath) }) {
raise(CurrencyStatusError.DataError(it))
}
}

View file

@ -6,6 +6,7 @@ import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.models.UserWalletId
@ -18,13 +19,15 @@ class GetCryptoCurrencyUseCase(
*
* @param userWalletId The ID of the user's wallet.
* @param id The ID of the cryptocurrency.
* @param derivationPath currency derivation path.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): Either<CurrencyStatusError, CryptoCurrency> {
return either { getCurrency(userWalletId, id) }
return either { getCurrency(userWalletId, id, derivationPath) }
}
/**
@ -40,9 +43,10 @@ class GetCryptoCurrencyUseCase(
private suspend fun Raise<CurrencyStatusError>.getCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency {
return catch(
block = { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, id) },
block = { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, id, derivationPath) },
catch = { raise(CurrencyStatusError.DataError(it)) },
)
}

View file

@ -5,6 +5,7 @@ import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
@ -32,20 +33,23 @@ class GetCurrencyStatusUpdatesUseCase(
*
* @param userWalletId The unique identifier of the user's wallet.
* @param currencyId The unique identifier of the cryptocurrency.
* @param derivationPath currency derivation path.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
operator fun invoke(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(getCurrency(userWalletId, currencyId))
emitAll(getCurrency(userWalletId, currencyId, derivationPath))
}.flowOn(dispatchers.io)
}
private suspend fun getCurrency(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
val operations = CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
@ -54,7 +58,7 @@ class GetCurrencyStatusUpdatesUseCase(
userWalletId = userWalletId,
)
return operations.getCurrencyStatusFlow(currencyId).map { maybeCurrency ->
return operations.getCurrencyStatusFlow(currencyId, derivationPath).map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}

View file

@ -24,12 +24,14 @@ class GetCurrencyWarningsUseCase(
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
derivationPath: Network.DerivationPath,
): Flow<Set<CryptoCurrencyWarning>> {
return combine(
getFeeWarningFlow(
userWalletId = userWalletId,
networkId = currency.network.id,
currencyId = currency.id,
derivationPath = derivationPath,
),
flowOf(walletManagersFacade.getRentInfo(userWalletId, currency.network)),
flowOf(walletManagersFacade.getExistentialDeposit(userWalletId, currency.network)),
@ -51,6 +53,7 @@ class GetCurrencyWarningsUseCase(
userWalletId: UserWalletId,
networkId: Network.ID,
currencyId: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): Flow<CryptoCurrencyWarning?> {
val operations = CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
@ -60,8 +63,8 @@ class GetCurrencyWarningsUseCase(
)
return combine(
operations.getCurrencyStatusFlow(currencyId).map { it.getOrNull() },
operations.getNetworkCoinFlow(networkId).map { it.getOrNull() },
operations.getCurrencyStatusFlow(currencyId, derivationPath).map { it.getOrNull() },
operations.getNetworkCoinFlow(networkId, derivationPath).map { it.getOrNull() },
) { tokenStatus, coinStatus ->
when {
tokenStatus != null && coinStatus != null -> {

View file

@ -23,12 +23,14 @@ class GetNetworkCoinStatusUseCase(
operator fun invoke(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
flow = getCurrency(
userWalletId = userWalletId,
networkId = networkId,
derivationPath = derivationPath,
),
)
}
@ -38,6 +40,7 @@ class GetNetworkCoinStatusUseCase(
private suspend fun getCurrency(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
val operations = CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
@ -46,7 +49,7 @@ class GetNetworkCoinStatusUseCase(
userWalletId = userWalletId,
)
return operations.getNetworkCoinFlow(networkId).map { maybeCurrency ->
return operations.getNetworkCoinFlow(networkId, derivationPath).map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}

View file

@ -67,18 +67,24 @@ internal class CurrenciesStatusesOperations(
}
}
suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow<Either<Error, CryptoCurrencyStatus>> {
suspend fun getCurrencyStatusFlow(
currencyId: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<Error, CryptoCurrencyStatus>> {
val currency = recover(
block = { getMultiCurrencyWalletCurrency(currencyId) },
block = { getMultiCurrencyWalletCurrency(currencyId, derivationPath) },
recover = { return flowOf(it.left()) },
)
return getCurrencyStatusFlow(currency)
}
suspend fun getNetworkCoinFlow(networkId: Network.ID): Flow<Either<Error, CryptoCurrencyStatus>> {
suspend fun getNetworkCoinFlow(
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): Flow<Either<Error, CryptoCurrencyStatus>> {
val currency = recover(
block = { getNetworkCoin(networkId) },
block = { getNetworkCoin(networkId, derivationPath) },
recover = { return flowOf(it.left()) },
)
@ -178,14 +184,26 @@ internal class CurrenciesStatusesOperations(
.onEmpty { emit(Error.EmptyCurrencies.left()) }
}
private suspend fun Raise<Error>.getMultiCurrencyWalletCurrency(currencyId: CryptoCurrency.ID): CryptoCurrency {
return Either.catch { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, currencyId) }
private suspend fun Raise<Error>.getMultiCurrencyWalletCurrency(
currencyId: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency {
return Either.catch {
currenciesRepository.getMultiCurrencyWalletCurrency(
userWalletId,
currencyId,
derivationPath,
)
}
.mapLeft { Error.DataError(it) }
.bind()
}
private suspend fun Raise<Error>.getNetworkCoin(networkId: Network.ID): CryptoCurrency {
return Either.catch { currenciesRepository.getNetworkCoin(userWalletId, networkId) }
private suspend fun Raise<Error>.getNetworkCoin(
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency {
return Either.catch { currenciesRepository.getNetworkCoin(userWalletId, networkId, derivationPath) }
.mapLeft { Error.DataError(it) }
.bind()
}

View file

@ -98,19 +98,29 @@ interface CurrenciesRepository {
*
* @param userWalletId The unique identifier of the user wallet.
* @param id The unique identifier of the cryptocurrency to be retrieved.
* @param derivationPath currency derivation path.
* @return The cryptocurrency associated with the user wallet and ID.
* @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet
* ID provided.
*/
suspend fun getMultiCurrencyWalletCurrency(userWalletId: UserWalletId, id: CryptoCurrency.ID): CryptoCurrency
suspend fun getMultiCurrencyWalletCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency
/**
* Get the coin for a specific network.
*
* @param userWalletId The unique identifier of the user wallet.
* @param networkId The unique identifier of the network.
* @param derivationPath currency derivation path.
*/
suspend fun getNetworkCoin(userWalletId: UserWalletId, networkId: Network.ID): CryptoCurrency.Coin
suspend fun getNetworkCoin(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency.Coin
/**
* Determines whether the tokens within a specific multi-currency user wallet are grouped.

View file

@ -67,6 +67,7 @@ internal class MockCurrenciesRepository(
override suspend fun getMultiCurrencyWalletCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency {
val token = token.getOrElse { e -> throw e }
@ -75,7 +76,11 @@ internal class MockCurrenciesRepository(
return token
}
override suspend fun getNetworkCoin(userWalletId: UserWalletId, networkId: Network.ID): CryptoCurrency.Coin {
override suspend fun getNetworkCoin(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): CryptoCurrency.Coin {
TODO("Not yet implemented")
}

View file

@ -140,6 +140,7 @@ internal class TokenDetailsViewModel @Inject constructor(
getCurrencyWarningsUseCase.invoke(
userWalletId = selectedWallet.walletId,
currency = cryptoCurrency,
derivationPath = cryptoCurrency.network.derivationPath,
)
.distinctUntilChanged()
.onEach { uiState = stateFactory.getStateWithNotifications(it) }
@ -151,6 +152,7 @@ internal class TokenDetailsViewModel @Inject constructor(
getCurrencyStatusUpdatesUseCase(
userWalletId = selectedWallet.walletId,
currencyId = cryptoCurrency.id,
derivationPath = cryptoCurrency.network.derivationPath,
)
.distinctUntilChanged()
.onEach { either ->
@ -247,6 +249,7 @@ internal class TokenDetailsViewModel @Inject constructor(
getNetworkCoinStatusUseCase(
userWalletId = wallet.walletId,
networkId = status.currency.network.id,
derivationPath = status.currency.network.derivationPath,
)
.take(count = 1)
.collectLatest {
@ -373,6 +376,7 @@ internal class TokenDetailsViewModel @Inject constructor(
fetchCurrencyStatusUseCase.invoke(
userWalletId = wallet.walletId,
id = cryptoCurrency.id,
derivationPath = cryptoCurrency.network.derivationPath,
refresh = true,
)
},

View file

@ -579,6 +579,7 @@ internal class WalletViewModel @Inject constructor(
getNetworkCoinStatusUseCase(
userWalletId = userWallet.walletId,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
)
.take(count = 1)
.collectLatest {