Updated on 2026-08-14
This commit is contained in:
commit
edec3fcdff
391 changed files with 10988 additions and 3221 deletions
|
|
@ -27,6 +27,7 @@ dependencies {
|
|||
api(projects.domain.common)
|
||||
api(projects.domain.models)
|
||||
api(projects.domain.tokens)
|
||||
api(projects.domain.wallets)
|
||||
// endregion
|
||||
|
||||
// region Project - Data
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ internal class CryptoPortfolioConverter @AssistedInject constructor(
|
|||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = tokens,
|
||||
userWallet = userWallet,
|
||||
accountIndex = value.derivationIndex.toDerivationIndex(),
|
||||
).toSet()
|
||||
} else {
|
||||
emptySet()
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import com.tangem.data.account.store.ArchivedAccountsStoreFactory
|
|||
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.account.WalletAccountsSaver
|
||||
import com.tangem.data.common.cache.etag.ETagsStore
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
|
|
@ -42,7 +42,6 @@ internal object AccountDataModule {
|
|||
accountsResponseStoreFactory: AccountsResponseStoreFactory,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
userTokensSaver: UserTokensSaver,
|
||||
eTagsStore: ETagsStore,
|
||||
accountConverterFactoryContainer: AccountConverterFactoryContainer,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): AccountsCRUDRepository {
|
||||
|
|
@ -53,7 +52,7 @@ internal object AccountDataModule {
|
|||
archivedAccountsStoreFactory = ArchivedAccountsStoreFactory,
|
||||
userWalletsStore = userWalletsStore,
|
||||
userTokensSaver = userTokensSaver,
|
||||
eTagsStore = eTagsStore,
|
||||
archivedAccountsETagStore = RuntimeStateStore(emptyMap()),
|
||||
convertersContainer = accountConverterFactoryContainer,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.data.account.di
|
||||
|
||||
import com.tangem.data.account.producer.DefaultSingleAccountProducer
|
||||
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface SingleAccountProducerFactoryModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindSingleAccountProducerFactory(impl: DefaultSingleAccountProducer.Factory): SingleAccountProducer.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.data.account.di
|
||||
|
||||
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||
import com.tangem.domain.account.supplier.SingleAccountSupplier
|
||||
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 SingleAccountSupplierModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSingleAccountSupplier(factory: SingleAccountProducer.Factory): SingleAccountSupplier {
|
||||
return object : SingleAccountSupplier(
|
||||
factory = factory,
|
||||
keyCreator = { "single_account_${it.accountId.value}" },
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import com.tangem.data.account.store.AccountsResponseStore
|
|||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||
import com.tangem.data.account.utils.assignTokens
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.account.WalletAccountsSaver
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
|
|
@ -19,6 +18,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
|||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.datasource.utils.getSyncOrNull
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -51,18 +51,25 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : WalletAccountsFetcher, WalletAccountsSaver {
|
||||
|
||||
override suspend fun fetch(userWalletId: UserWalletId) {
|
||||
override suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse {
|
||||
val savedAccountsResponse = getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
|
||||
val accountsResponse = fetchWalletAccounts(userWalletId, savedAccountsResponse)
|
||||
?: return
|
||||
|
||||
if (accountsResponse.accounts.isEmpty()) {
|
||||
initializeAccounts(userWalletId, accountsResponse)
|
||||
} else if (accountsResponse.unassignedTokens.isNotEmpty()) {
|
||||
assignTokens(userWalletId, accountsResponse)
|
||||
return when {
|
||||
accountsResponse.accounts.isEmpty() -> {
|
||||
initializeAccounts(userWalletId, accountsResponse)
|
||||
}
|
||||
accountsResponse.unassignedTokens.isNotEmpty() -> {
|
||||
assignTokens(userWalletId, accountsResponse)
|
||||
}
|
||||
else -> accountsResponse
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse? {
|
||||
return getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse) {
|
||||
val store = getAccountsResponseStore(userWalletId = userWalletId)
|
||||
|
||||
|
|
@ -115,7 +122,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
|||
private suspend fun fetchWalletAccounts(
|
||||
userWalletId: UserWalletId,
|
||||
savedAccountsResponse: GetWalletAccountsResponse?,
|
||||
): GetWalletAccountsResponse? {
|
||||
): GetWalletAccountsResponse {
|
||||
return safeApiCall(
|
||||
call = {
|
||||
val apiResponse = withContext(dispatchers.io) {
|
||||
|
|
@ -145,7 +152,10 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun initializeAccounts(userWalletId: UserWalletId, accountsResponse: GetWalletAccountsResponse) {
|
||||
private suspend fun initializeAccounts(
|
||||
userWalletId: UserWalletId,
|
||||
accountsResponse: GetWalletAccountsResponse,
|
||||
): GetWalletAccountsResponse {
|
||||
val response = defaultWalletAccountsResponseFactory.create(
|
||||
userWalletId = userWalletId,
|
||||
userTokensResponse = UserTokensResponse(
|
||||
|
|
@ -156,14 +166,17 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
|||
)
|
||||
|
||||
userTokensSaver.push(userWalletId = userWalletId, response = response.toUserTokensResponse())
|
||||
val syncedResponse = push(userWalletId = userWalletId, accounts = response.accounts)
|
||||
val syncedResponse = push(userWalletId = userWalletId, accounts = response.accounts) ?: response
|
||||
|
||||
if (syncedResponse != null) {
|
||||
store(userWalletId = userWalletId, response = syncedResponse)
|
||||
}
|
||||
store(userWalletId = userWalletId, response = syncedResponse)
|
||||
|
||||
return syncedResponse
|
||||
}
|
||||
|
||||
private suspend fun assignTokens(userWalletId: UserWalletId, accountsResponse: GetWalletAccountsResponse) {
|
||||
private suspend fun assignTokens(
|
||||
userWalletId: UserWalletId,
|
||||
accountsResponse: GetWalletAccountsResponse,
|
||||
): GetWalletAccountsResponse {
|
||||
val accountsResponseWithTokens = accountsResponse.assignTokens(userWalletId)
|
||||
|
||||
store(userWalletId = userWalletId, response = accountsResponseWithTokens)
|
||||
|
|
@ -172,6 +185,8 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
|||
userWalletId = userWalletId,
|
||||
response = accountsResponseWithTokens.toUserTokensResponse(),
|
||||
)
|
||||
|
||||
return accountsResponseWithTokens
|
||||
}
|
||||
|
||||
private suspend fun getETag(userWalletId: UserWalletId): String? {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.data.account.fetcher
|
||||
|
||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.currency.UserTokensResponseAccountIdEnricher
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
|
|
@ -10,6 +9,7 @@ import com.tangem.datasource.api.common.response.isNetworkError
|
|||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import timber.log.Timber
|
||||
|
|
@ -49,11 +49,13 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
|
|||
savedAccountsResponse: GetWalletAccountsResponse?,
|
||||
pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse?,
|
||||
storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit,
|
||||
): GetWalletAccountsResponse? {
|
||||
): GetWalletAccountsResponse {
|
||||
val isResponseUpToDate = error.isNetworkError(code = Code.NOT_MODIFIED)
|
||||
if (isResponseUpToDate) {
|
||||
Timber.e("ETag is up to date, no need to update accounts for wallet: $userWalletId")
|
||||
return savedAccountsResponse
|
||||
return requireNotNull(savedAccountsResponse) {
|
||||
"Saved accounts response is null for wallet: $userWalletId"
|
||||
}
|
||||
}
|
||||
|
||||
var response = savedAccountsResponse ?: createDefaultResponse(userWalletId)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package com.tangem.data.account.producer
|
|||
import arrow.core.Option
|
||||
import arrow.core.some
|
||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
|
||||
|
|
@ -48,12 +48,16 @@ internal class AccountListCryptoCurrenciesProducer @AssistedInject constructor(
|
|||
.map { response ->
|
||||
if (response == null) return@map emptySet()
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
response = response.toUserTokensResponse(),
|
||||
userWallet = userWallet,
|
||||
).toSet()
|
||||
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = accountDTO.tokens.orEmpty(),
|
||||
userWallet = userWallet,
|
||||
accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
|
||||
)
|
||||
}
|
||||
}
|
||||
.onEmpty { emit(emptySet()) }
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.data.account.producer
|
||||
|
||||
import arrow.core.Option
|
||||
import arrow.core.none
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.account.producer.SingleAccountProducer
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Default implementation of [SingleAccountProducer] that produces a flow of [Account.CryptoPortfolio]
|
||||
* for a single account identified by [SingleAccountProducer.Params.accountId].
|
||||
*
|
||||
* It uses [SingleAccountListSupplier] to get the list of accounts and filters it to find the
|
||||
* specific account.
|
||||
*
|
||||
* @property params Parameters containing the account ID for which the portfolio is produced.
|
||||
* @property singleAccountListSupplier Supplier to get the list of accounts.
|
||||
* @property dispatchers Coroutine dispatcher provider for managing threading.
|
||||
*/
|
||||
internal class DefaultSingleAccountProducer @AssistedInject constructor(
|
||||
@Assisted val params: SingleAccountProducer.Params,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SingleAccountProducer {
|
||||
|
||||
override val fallback: Option<Account.CryptoPortfolio>
|
||||
get() = none()
|
||||
|
||||
override fun produce(): Flow<Account.CryptoPortfolio> {
|
||||
return singleAccountListSupplier(
|
||||
params = SingleAccountListProducer.Params(userWalletId = params.accountId.userWalletId),
|
||||
)
|
||||
.mapNotNull { accountList ->
|
||||
accountList.accounts.firstOrNull {
|
||||
it is Account.CryptoPortfolio && params.accountId == it.accountId
|
||||
} as? Account.CryptoPortfolio
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : SingleAccountProducer.Factory {
|
||||
override fun create(params: SingleAccountProducer.Params): DefaultSingleAccountProducer
|
||||
}
|
||||
}
|
||||
|
|
@ -9,13 +9,16 @@ import com.tangem.data.account.store.AccountsResponseStore
|
|||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||
import com.tangem.data.account.store.ArchivedAccountsStore
|
||||
import com.tangem.data.account.store.ArchivedAccountsStoreFactory
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.account.WalletAccountsSaver
|
||||
import com.tangem.data.common.cache.etag.ETagsStore
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException
|
||||
import com.tangem.datasource.api.common.response.ETAG_HEADER
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.utils.getSyncOrNull
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
|
|
@ -43,7 +46,7 @@ internal class DefaultAccountsCRUDRepository(
|
|||
private val archivedAccountsStoreFactory: ArchivedAccountsStoreFactory,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val userTokensSaver: UserTokensSaver,
|
||||
private val eTagsStore: ETagsStore,
|
||||
private val archivedAccountsETagStore: RuntimeStateStore<Map<String, String?>>,
|
||||
private val convertersContainer: AccountConverterFactoryContainer,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : AccountsCRUDRepository {
|
||||
|
|
@ -90,19 +93,38 @@ internal class DefaultAccountsCRUDRepository(
|
|||
}
|
||||
|
||||
override suspend fun fetchArchivedAccounts(userWalletId: UserWalletId) {
|
||||
val response = withContext(dispatchers.io) {
|
||||
tangemTechApi.getWalletArchivedAccounts(
|
||||
walletId = userWalletId.stringValue,
|
||||
eTag = getETag(userWalletId),
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
val eTag = archivedAccountsETagStore.getSyncOrNull()?.get(key = userWalletId.stringValue)
|
||||
val store = getArchivedAccountsStore(userWalletId = userWalletId)
|
||||
val converter = ArchivedAccountConverter(userWalletId = userWalletId)
|
||||
|
||||
val archivedAccounts = converter.convertList(input = response.accounts)
|
||||
val response = safeApiCall(
|
||||
call = {
|
||||
val apiResponse = withContext(dispatchers.io) {
|
||||
tangemTechApi.getWalletArchivedAccounts(
|
||||
walletId = userWalletId.stringValue,
|
||||
eTag = eTag,
|
||||
)
|
||||
}
|
||||
|
||||
store.store(value = archivedAccounts)
|
||||
saveETag(userWalletId, apiResponse)
|
||||
|
||||
apiResponse.bind()
|
||||
},
|
||||
onError = {
|
||||
if (it is HttpException && it.code == HttpException.Code.NOT_MODIFIED) {
|
||||
null
|
||||
} else {
|
||||
throw it
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (response != null) {
|
||||
val converter = ArchivedAccountConverter(userWalletId = userWalletId)
|
||||
|
||||
val archivedAccounts = converter.convertList(input = response.accounts)
|
||||
|
||||
store.store(value = archivedAccounts)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveAccounts(accountList: AccountList) {
|
||||
|
|
@ -154,9 +176,17 @@ internal class DefaultAccountsCRUDRepository(
|
|||
return accountListResponse.wallet.totalAccounts.toOption()
|
||||
}
|
||||
|
||||
override fun getTotalAccountsCount(userWalletId: UserWalletId): Flow<Option<Int>> {
|
||||
override suspend fun getTotalActiveAccountsCountSync(userWalletId: UserWalletId): Option<Int> = option {
|
||||
val accountListResponse = getAccountsResponseSync(userWalletId = userWalletId)
|
||||
|
||||
ensureNotNull(accountListResponse)
|
||||
|
||||
return accountListResponse.accounts.size.toOption()
|
||||
}
|
||||
|
||||
override fun getTotalActiveAccountsCount(userWalletId: UserWalletId): Flow<Option<Int>> {
|
||||
return getAccountsResponseStore(userWalletId = userWalletId).data
|
||||
.map { it?.wallet?.totalAccounts.toOption() }
|
||||
.map { it?.accounts?.size.toOption() }
|
||||
}
|
||||
|
||||
override fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||
|
|
@ -167,8 +197,12 @@ internal class DefaultAccountsCRUDRepository(
|
|||
|
||||
override fun getUserWalletsSync(): List<UserWallet> = userWalletsStore.userWalletsSync
|
||||
|
||||
private suspend fun getETag(userWalletId: UserWalletId): String? {
|
||||
return eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts)
|
||||
private suspend fun saveETag(userWalletId: UserWalletId, apiResponse: ApiResponse<*>) {
|
||||
val eTag = apiResponse.headers[ETAG_HEADER]?.firstOrNull()
|
||||
|
||||
archivedAccountsETagStore.update {
|
||||
it + (userWalletId.stringValue to eTag)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getAccountsResponseSync(userWalletId: UserWalletId): GetWalletAccountsResponse? {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||
import com.tangem.data.account.utils.assignTokens
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.datasource.utils.getSyncOrNull
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
|
|
@ -78,6 +78,8 @@ internal class DefaultMainAccountTokensMigration(
|
|||
},
|
||||
)
|
||||
|
||||
store.updateData { updatedResponse }
|
||||
|
||||
userTokensSaver.push(
|
||||
userWalletId = userWalletId,
|
||||
response = updatedResponse.toUserTokensResponse(),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package com.tangem.data.account.utils
|
||||
|
||||
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import javax.inject.Inject
|
||||
|
|
@ -19,7 +21,7 @@ import javax.inject.Inject
|
|||
* @property userWalletsListRepository repository to get user wallet information
|
||||
* @property cryptoPortfolioCF converter factory to convert crypto portfolio accounts
|
||||
* @property userTokensResponseFactory factory to create [UserTokensResponse]
|
||||
* @property cardCryptoCurrencyFactory factory to get default coins for multi-currency wallet
|
||||
* @property networkFactory factory to create network derivation path
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
|
@ -27,7 +29,7 @@ internal class DefaultWalletAccountsResponseFactory @Inject constructor(
|
|||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory,
|
||||
private val userTokensResponseFactory: UserTokensResponseFactory,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val networkFactory: NetworkFactory,
|
||||
) {
|
||||
|
||||
suspend fun create(userWalletId: UserWalletId, userTokensResponse: UserTokensResponse?): GetWalletAccountsResponse {
|
||||
|
|
@ -59,10 +61,12 @@ internal class DefaultWalletAccountsResponseFactory @Inject constructor(
|
|||
private fun UserTokensResponse?.orDefault(userWallet: UserWallet?): UserTokensResponse {
|
||||
if (this != null) return this
|
||||
|
||||
return userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = userWallet?.let(cardCryptoCurrencyFactory::createDefaultCoinsForMultiCurrencyWallet).orEmpty(),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
return userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = userWallet,
|
||||
networkFactory = networkFactory,
|
||||
accountId = userWallet?.let {
|
||||
AccountId.forCryptoPortfolio(userWalletId = it.walletId, derivationIndex = DerivationIndex.Main)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,20 +6,6 @@ import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResp
|
|||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/** Flattens the tokens from all wallet accounts into a single list */
|
||||
internal fun GetWalletAccountsResponse.flattenTokens(): List<UserTokensResponse.Token> {
|
||||
return accounts.flatMap { it.tokens.orEmpty() }
|
||||
}
|
||||
|
||||
/** Converts the [GetWalletAccountsResponse] into a [UserTokensResponse] */
|
||||
internal fun GetWalletAccountsResponse.toUserTokensResponse(): UserTokensResponse {
|
||||
return UserTokensResponse(
|
||||
group = wallet.group,
|
||||
sort = wallet.sort,
|
||||
tokens = flattenTokens(),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns tokens from a [UserTokensResponse] to the wallet accounts in the [GetWalletAccountsResponse]
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class DefaultSingleAccountListFetcherTest {
|
|||
// Arrange
|
||||
val params = SingleAccountListFetcher.Params(userWalletId = userWalletId)
|
||||
|
||||
coEvery { walletAccountsFetcher.fetch(userWalletId) } returns mockk()
|
||||
|
||||
// Act
|
||||
val actual = fetcher.invoke(params)
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ class DefaultWalletAccountsFetcherTest {
|
|||
@Test
|
||||
fun `fetch should call error handler when getWalletAccounts returns error`() = runTest {
|
||||
// Arrange
|
||||
val savedAccountsResponse = null
|
||||
val savedAccountsResponse = createGetWalletAccountsResponse(userWalletId)
|
||||
val apiError = ApiResponse.Error(ApiResponseError.NetworkException())
|
||||
|
||||
accountsResponseStoreFlow.value = savedAccountsResponse
|
||||
|
|
@ -212,7 +212,7 @@ class DefaultWalletAccountsFetcherTest {
|
|||
fetchWalletAccountsErrorHandler.handle(
|
||||
error = apiError.cause,
|
||||
userWalletId = userWalletId,
|
||||
savedAccountsResponse = null,
|
||||
savedAccountsResponse = savedAccountsResponse,
|
||||
pushWalletAccounts = any(),
|
||||
storeWalletAccounts = any(),
|
||||
)
|
||||
|
|
@ -231,7 +231,7 @@ class DefaultWalletAccountsFetcherTest {
|
|||
fetchWalletAccountsErrorHandler.handle(
|
||||
error = apiError.cause,
|
||||
userWalletId = userWalletId,
|
||||
savedAccountsResponse = null,
|
||||
savedAccountsResponse = savedAccountsResponse,
|
||||
pushWalletAccounts = any(),
|
||||
storeWalletAccounts = any(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ package com.tangem.data.account.fetcher
|
|||
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException.Code
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import io.mockk.clearMocks
|
||||
|
|
@ -53,6 +53,8 @@ class FetchWalletAccountsErrorHandlerTest {
|
|||
@Test
|
||||
fun `does not update accounts when response is up to date`() = runTest {
|
||||
// Arrange
|
||||
val response = createGetWalletAccountsResponse(userWalletId)
|
||||
|
||||
val error = ApiResponseError.HttpException(
|
||||
code = Code.NOT_MODIFIED,
|
||||
message = "Not Modified",
|
||||
|
|
@ -63,7 +65,7 @@ class FetchWalletAccountsErrorHandlerTest {
|
|||
handler.handle(
|
||||
error = error,
|
||||
userWalletId = userWalletId,
|
||||
savedAccountsResponse = null,
|
||||
savedAccountsResponse = response,
|
||||
pushWalletAccounts = pushWalletAccounts,
|
||||
storeWalletAccounts = storeWalletAccounts,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.data.account.store.AccountsResponseStoreFactory
|
|||
import com.tangem.data.account.store.ArchivedAccountsStore
|
||||
import com.tangem.data.account.store.ArchivedAccountsStoreFactory
|
||||
import com.tangem.data.common.account.WalletAccountsSaver
|
||||
import com.tangem.data.common.cache.etag.ETagsStore
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
|
|
@ -54,7 +53,7 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
|
||||
private val userWalletsStore: UserWalletsStore = mockk()
|
||||
private val userTokensSaver: UserTokensSaver = mockk()
|
||||
private val eTagsStore: ETagsStore = mockk()
|
||||
private val archivedAccountsETagStore: RuntimeStateStore<Map<String, String?>> = mockk(relaxUnitFun = true)
|
||||
|
||||
private val convertersContainer: AccountConverterFactoryContainer = mockk()
|
||||
private val accountListConverter: AccountListConverter = mockk()
|
||||
|
|
@ -67,7 +66,7 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
archivedAccountsStoreFactory = archivedAccountsStoreFactory,
|
||||
userWalletsStore = userWalletsStore,
|
||||
userTokensSaver = userTokensSaver,
|
||||
eTagsStore = eTagsStore,
|
||||
archivedAccountsETagStore = archivedAccountsETagStore,
|
||||
convertersContainer = convertersContainer,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
|
@ -532,7 +531,7 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
|
||||
val archivedAccount = ArchivedAccountConverter(userWalletId).convert(accountDTO)
|
||||
|
||||
coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag
|
||||
coEvery { archivedAccountsETagStore.getSyncOrNull() } returns mapOf(userWalletId.stringValue to eTag)
|
||||
|
||||
coEvery {
|
||||
tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag)
|
||||
|
|
@ -546,9 +545,10 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
Truth.assertThat(actual).containsExactly(archivedAccount)
|
||||
|
||||
coVerifyOrder {
|
||||
eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts)
|
||||
tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag)
|
||||
archivedAccountsETagStore.getSyncOrNull()
|
||||
archivedAccountsStoreFactory.create(userWalletId)
|
||||
tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag)
|
||||
archivedAccountsETagStore.update(any())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +558,7 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
val eTag = "etag123"
|
||||
val exception = Exception("API error")
|
||||
|
||||
coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag
|
||||
coEvery { archivedAccountsETagStore.getSyncOrNull() } returns mapOf(userWalletId.stringValue to eTag)
|
||||
coEvery { tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag) } throws exception
|
||||
|
||||
// Act
|
||||
|
|
@ -570,7 +570,7 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
Truth.assertThat(archivedAccountsStore.getSyncOrNull()).isNull()
|
||||
|
||||
coVerifyOrder {
|
||||
eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts)
|
||||
archivedAccountsETagStore.getSyncOrNull()
|
||||
tangemTechApi.getWalletArchivedAccounts(userWalletId.stringValue, eTag)
|
||||
}
|
||||
}
|
||||
|
|
@ -644,15 +644,15 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetTotalAccountsCountSync {
|
||||
inner class GetTotalActiveAccountsCountSync {
|
||||
|
||||
@Test
|
||||
fun `getTotalAccountsCountSync returns None if account list response is null`() = runTest {
|
||||
fun `getTotalActiveAccountsCountSync returns None if account list response is null`() = runTest {
|
||||
// Arrange
|
||||
accountsResponseStoreFlow.value = null
|
||||
|
||||
// Act
|
||||
val actual = repository.getTotalAccountsCountSync(userWalletId)
|
||||
val actual = repository.getTotalActiveAccountsCountSync(userWalletId)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(None)
|
||||
|
|
@ -664,20 +664,22 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `getTotalAccountsCountSync returns Some with totalAccounts when response is valid`() = runTest {
|
||||
fun `getTotalActiveAccountsCountSync returns Some with totalAccounts when response is valid`() = runTest {
|
||||
// Arrange
|
||||
val totalAccounts = 5
|
||||
val response = mockk<GetWalletAccountsResponse> {
|
||||
every { this@mockk.wallet.totalAccounts } returns totalAccounts
|
||||
val response = createGetWalletAccountsResponse(userWalletId).let {
|
||||
it.copy(
|
||||
wallet = it.wallet.copy(totalAccounts = totalAccounts),
|
||||
)
|
||||
}
|
||||
|
||||
accountsResponseStoreFlow.value = response
|
||||
|
||||
// Act
|
||||
val actual = repository.getTotalAccountsCountSync(userWalletId)
|
||||
val actual = repository.getTotalActiveAccountsCountSync(userWalletId)
|
||||
|
||||
// Assert
|
||||
val expected = totalAccounts.toOption()
|
||||
val expected = 1.toOption()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
verifyOrder {
|
||||
|
|
@ -689,15 +691,15 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetTotalAccountsCount {
|
||||
inner class GetTotalActiveAccountsCount {
|
||||
|
||||
@Test
|
||||
fun `getTotalAccountsCount emits 0 when account list response is null`() = runTest {
|
||||
fun `getTotalActiveAccountsCount emits 0 when account list response is null`() = runTest {
|
||||
// Arrange
|
||||
accountsResponseStoreFlow.value = null
|
||||
|
||||
// Act
|
||||
val flow = repository.getTotalAccountsCount(userWalletId)
|
||||
val flow = repository.getTotalActiveAccountsCount(userWalletId)
|
||||
val actual = getEmittedValues(flow)
|
||||
|
||||
// Assert
|
||||
|
|
@ -710,21 +712,23 @@ class DefaultAccountsCRUDRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `getTotalAccountsCount emits correct value when response is valid`() = runTest {
|
||||
fun `getTotalActiveAccountsCount emits correct value when response is valid`() = runTest {
|
||||
// Arrange
|
||||
val totalAccounts = 7
|
||||
val response = mockk<GetWalletAccountsResponse> {
|
||||
every { this@mockk.wallet.totalAccounts } returns totalAccounts
|
||||
val response = createGetWalletAccountsResponse(userWalletId).let {
|
||||
it.copy(
|
||||
wallet = it.wallet.copy(totalAccounts = totalAccounts),
|
||||
)
|
||||
}
|
||||
|
||||
accountsResponseStoreFlow.value = response
|
||||
|
||||
// Act
|
||||
val flow = repository.getTotalAccountsCount(userWalletId)
|
||||
val flow = repository.getTotalActiveAccountsCount(userWalletId)
|
||||
val actual = getEmittedValues(flow)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).containsExactly(totalAccounts.toOption())
|
||||
Truth.assertThat(actual).containsExactly(1.toOption())
|
||||
verifyOrder {
|
||||
accountsResponseStoreFactory.create(userWalletId)
|
||||
accountsResponseStore.data
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import com.tangem.data.account.converter.createWalletAccountDTO
|
|||
import com.tangem.data.account.store.AccountsResponseStore
|
||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
||||
import com.tangem.data.account.utils.toUserTokensResponse
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -183,6 +183,8 @@ class DefaultMainAccountTokensMigrationTest {
|
|||
|
||||
accountsResponseStoreFlow.value = response
|
||||
|
||||
coEvery { accountsResponseStore.updateData(any()) } returns mockk()
|
||||
|
||||
// Act
|
||||
val actual = migration.migrate(userWalletId, derivationIndex)
|
||||
|
||||
|
|
@ -199,6 +201,7 @@ class DefaultMainAccountTokensMigrationTest {
|
|||
coVerifySequence {
|
||||
accountsResponseStoreFactory.create(userWalletId)
|
||||
accountsResponseStore.data
|
||||
accountsResponseStore.updateData(any())
|
||||
userTokensSaver.push(
|
||||
userWalletId = userWalletId,
|
||||
response = migratedResponse.toUserTokensResponse(),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.data.account.utils
|
|||
import com.google.common.truth.Truth
|
||||
import com.tangem.data.account.converter.CryptoPortfolioConverter
|
||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
|
|
@ -27,13 +27,13 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
private val cryptoPortfolioCF = mockk<CryptoPortfolioConverter.Factory>()
|
||||
private val cryptoPortfolioConverter = mockk<CryptoPortfolioConverter>()
|
||||
private val userTokensResponseFactory = mockk<UserTokensResponseFactory>()
|
||||
private val cardCryptoCurrencyFactory = mockk<CardCryptoCurrencyFactory>()
|
||||
private val networkFactory = mockk<NetworkFactory>()
|
||||
|
||||
private val factory = DefaultWalletAccountsResponseFactory(
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
cryptoPortfolioCF = cryptoPortfolioCF,
|
||||
userTokensResponseFactory = userTokensResponseFactory,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
networkFactory = networkFactory,
|
||||
)
|
||||
|
||||
private val userWalletId = UserWalletId("011")
|
||||
|
|
@ -50,7 +50,7 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
cryptoPortfolioCF,
|
||||
cryptoPortfolioConverter,
|
||||
userTokensResponseFactory,
|
||||
cardCryptoCurrencyFactory,
|
||||
networkFactory,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -65,10 +65,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
|
||||
coEvery { userWalletsListRepository.userWalletsSync() } returns emptyList()
|
||||
every {
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = emptyList(),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = null,
|
||||
networkFactory = networkFactory,
|
||||
accountId = null,
|
||||
)
|
||||
} returns userTokensResponse
|
||||
|
||||
|
|
@ -89,10 +89,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
|
||||
coVerifyOrder {
|
||||
userWalletsListRepository.userWalletsSync()
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = emptyList(),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = null,
|
||||
networkFactory = networkFactory,
|
||||
accountId = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -104,27 +104,24 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
every { walletId } returns userWalletId
|
||||
}
|
||||
|
||||
val defaultCoins = listOf(mockk<CryptoCurrency.Coin>())
|
||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
|
||||
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
||||
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns defaultCoins
|
||||
|
||||
val defaultResponse = UserTokensResponse(
|
||||
group = UserTokensResponse.GroupType.NETWORK,
|
||||
sort = UserTokensResponse.SortType.BALANCE,
|
||||
tokens = listOf(mockk(relaxed = true)),
|
||||
)
|
||||
|
||||
every {
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = defaultCoins,
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = userWallet,
|
||||
networkFactory = networkFactory,
|
||||
accountId = accounts.first().accountId,
|
||||
)
|
||||
} returns defaultResponse
|
||||
|
||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
|
||||
val accountsDTO = createWalletAccountDTO(userWalletId)
|
||||
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountsDTO)
|
||||
|
||||
|
|
@ -147,11 +144,10 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
coVerifyOrder {
|
||||
userWalletsListRepository.userWalletsSync()
|
||||
cryptoPortfolioConverter.convertListBack(accounts)
|
||||
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet)
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = defaultCoins,
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = userWallet,
|
||||
networkFactory = networkFactory,
|
||||
accountId = accounts.first().accountId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -162,22 +158,26 @@ class DefaultWalletAccountsResponseFactoryTest {
|
|||
val userWallet = mockk<UserWallet>(relaxed = true) {
|
||||
every { walletId } returns userWalletId
|
||||
}
|
||||
|
||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
|
||||
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
|
||||
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns emptyList()
|
||||
|
||||
val defaultResponse = UserTokensResponse(
|
||||
group = UserTokensResponse.GroupType.NETWORK,
|
||||
sort = UserTokensResponse.SortType.BALANCE,
|
||||
tokens = emptyList(),
|
||||
)
|
||||
|
||||
every {
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = emptyList(),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
userTokensResponseFactory.createDefaultResponse(
|
||||
userWallet = userWallet,
|
||||
networkFactory = networkFactory,
|
||||
accountId = accounts.first().accountId,
|
||||
)
|
||||
} returns defaultResponse
|
||||
val accounts = AccountList.empty(userWallet.walletId).accounts
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
|
||||
every { cryptoPortfolioConverter.convertListBack(accounts) } returns emptyList()
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import com.google.common.truth.Truth
|
|||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
|
||||
import com.tangem.datasource.api.tangemTech.models.account.flattenTokens
|
||||
import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.data.common.account
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
@ -11,5 +12,8 @@ interface WalletAccountsFetcher {
|
|||
|
||||
/** Fetch wallet accounts by [userWalletId] */
|
||||
@Throws
|
||||
suspend fun fetch(userWalletId: UserWalletId)
|
||||
suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse
|
||||
|
||||
/** Get saved wallet accounts by [userWalletId] */
|
||||
suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse?
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -18,7 +19,7 @@ class CryptoCurrencyFactory(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
|
||||
private val networkFactory by lazy(LazyThreadSafetyMode.NONE) { NetworkFactory(excludedBlockchains) }
|
||||
val networkFactory by lazy(LazyThreadSafetyMode.NONE) { NetworkFactory(excludedBlockchains) }
|
||||
|
||||
@Suppress("LongParameterList") // Yep, it's long
|
||||
fun createToken(
|
||||
|
|
@ -48,6 +49,7 @@ class CryptoCurrencyFactory(
|
|||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): CryptoCurrency.Token? {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
|
|
@ -58,6 +60,7 @@ class CryptoCurrencyFactory(
|
|||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
|
@ -74,11 +77,16 @@ class CryptoCurrencyFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun createCoin(chainId: Int, extraDerivationPath: String?, userWallet: UserWallet): CryptoCurrency.Coin? {
|
||||
fun createCoin(
|
||||
chainId: Int,
|
||||
extraDerivationPath: String?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): CryptoCurrency.Coin? {
|
||||
val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain
|
||||
|
||||
return if (blockchain != null) {
|
||||
createCoin(blockchain, extraDerivationPath, userWallet)
|
||||
createCoin(blockchain, extraDerivationPath, userWallet, accountIndex)
|
||||
} else {
|
||||
Timber.e("Unable to get blockchain from chainId == $chainId")
|
||||
null
|
||||
|
|
@ -89,6 +97,7 @@ class CryptoCurrencyFactory(
|
|||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): CryptoCurrency.Coin? {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
|
|
@ -99,6 +108,7 @@ class CryptoCurrencyFactory(
|
|||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
return createCoin(network)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package com.tangem.data.common.currency
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.tokens.getDefaultWalletBlockchains
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -23,10 +25,13 @@ import com.tangem.domain.models.wallet.isMultiCurrency
|
|||
* @property userWalletsStore user wallets store
|
||||
* @property userTokensResponseStore user tokens response store
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultCardCryptoCurrencyFactory(
|
||||
private val demoConfig: DemoConfig,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||
private val userTokensResponseStore: UserTokensResponseStore,
|
||||
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
||||
) : CardCryptoCurrencyFactory {
|
||||
|
|
@ -99,25 +104,7 @@ internal class DefaultCardCryptoCurrencyFactory(
|
|||
override fun createDefaultCoinsForMultiCurrencyWallet(userWallet: UserWallet): List<CryptoCurrency.Coin> {
|
||||
require(userWallet.isMultiCurrency) { "It isn't multi-currency wallet" }
|
||||
|
||||
val blockchains = when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
val card = userWallet.scanResponse.card
|
||||
|
||||
var blockchainsInternal = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
demoConfig.demoBlockchains
|
||||
} else {
|
||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
|
||||
if (card.isTestCard) {
|
||||
blockchainsInternal = blockchainsInternal.mapNotNull { it.getTestnetVersion() }
|
||||
}
|
||||
|
||||
blockchainsInternal
|
||||
}
|
||||
|
||||
is UserWallet.Hot -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
val blockchains = getDefaultWalletBlockchains(userWallet, demoConfig)
|
||||
|
||||
return blockchains.mapNotNull {
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
|
|
@ -152,15 +139,32 @@ internal class DefaultCardCryptoCurrencyFactory(
|
|||
userWallet: UserWallet,
|
||||
networks: Set<Network>,
|
||||
): Map<Network, List<CryptoCurrency>> {
|
||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
val existingNetworkWithCurrencies = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val response = walletAccountsFetcher.getSaved(userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
|
||||
val existingNetworkWithCurrencies = responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = response.tokens.filter { token ->
|
||||
networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath }
|
||||
},
|
||||
userWallet = userWallet,
|
||||
)
|
||||
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = accountDTO.tokens.orEmpty().filter { token ->
|
||||
networks.any {
|
||||
it.backendId == token.networkId && it.derivationPath.value == token.derivationPath
|
||||
}
|
||||
},
|
||||
userWallet = userWallet,
|
||||
accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = response.tokens.filter { token ->
|
||||
networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath }
|
||||
},
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
.groupBy(CryptoCurrency::network)
|
||||
|
||||
return networks.associateWith { emptyList<CryptoCurrency>() } + existingNetworkWithCurrencies
|
||||
|
|
@ -170,15 +174,28 @@ internal class DefaultCardCryptoCurrencyFactory(
|
|||
userWallet: UserWallet,
|
||||
rawIds: Set<Network.RawID>,
|
||||
): Map<Network.RawID, List<CryptoCurrency>> {
|
||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
|
||||
val networkIds = rawIds.map { it.toBlockchain().toNetworkId() }
|
||||
|
||||
return responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = response.tokens.filter { token -> token.networkId in networkIds },
|
||||
userWallet = userWallet,
|
||||
)
|
||||
return if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val response = walletAccountsFetcher.getSaved(userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
|
||||
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = accountDTO.tokens.orEmpty().filter { token -> token.networkId in networkIds },
|
||||
userWallet = userWallet,
|
||||
accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
tokens = response.tokens.filter { token -> token.networkId in networkIds },
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
.groupBy { it.network.id.rawId }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.blockchainsdk.utils.toCoinId
|
|||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import timber.log.Timber
|
||||
|
|
@ -17,26 +18,31 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
|
|||
private val networkFactory: NetworkFactory,
|
||||
) {
|
||||
|
||||
fun createCurrency(currencyId: String, response: UserTokensResponse, userWallet: UserWallet): CryptoCurrency {
|
||||
return response.tokens
|
||||
.asSequence()
|
||||
.mapNotNull { createCurrency(it, userWallet) }
|
||||
.first { it.id.value == currencyId }
|
||||
fun createCurrencies(
|
||||
response: UserTokensResponse,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): List<CryptoCurrency> {
|
||||
return createCurrencies(tokens = response.tokens, userWallet = userWallet, accountIndex = accountIndex)
|
||||
}
|
||||
|
||||
fun createCurrencies(response: UserTokensResponse, userWallet: UserWallet): List<CryptoCurrency> {
|
||||
return createCurrencies(tokens = response.tokens, userWallet = userWallet)
|
||||
}
|
||||
|
||||
fun createCurrencies(tokens: List<UserTokensResponse.Token>, userWallet: UserWallet): List<CryptoCurrency> {
|
||||
fun createCurrencies(
|
||||
tokens: List<UserTokensResponse.Token>,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): List<CryptoCurrency> {
|
||||
return tokens
|
||||
.asSequence()
|
||||
.mapNotNull { createCurrency(it, userWallet) }
|
||||
.mapNotNull { createCurrency(it, userWallet, accountIndex) }
|
||||
.distinctBy(CryptoCurrency::id)
|
||||
.toList()
|
||||
}
|
||||
|
||||
fun createCurrency(responseToken: UserTokensResponse.Token, userWallet: UserWallet): CryptoCurrency? {
|
||||
fun createCurrency(
|
||||
responseToken: UserTokensResponse.Token,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): CryptoCurrency? {
|
||||
var blockchain = Blockchain.fromNetworkId(responseToken.networkId)
|
||||
if (blockchain == null || blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}")
|
||||
|
|
@ -49,9 +55,9 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
|
|||
|
||||
val sdkToken = createSdkToken(responseToken)
|
||||
return if (sdkToken == null) {
|
||||
createCoin(blockchain, responseToken, userWallet)
|
||||
createCoin(blockchain, responseToken, userWallet, accountIndex)
|
||||
} else {
|
||||
createToken(blockchain, sdkToken, responseToken.derivationPath, userWallet)
|
||||
createToken(blockchain, sdkToken, responseToken.derivationPath, userWallet, accountIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,11 +77,13 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
|
|||
blockchain: Blockchain,
|
||||
responseToken: UserTokensResponse.Token,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): CryptoCurrency.Coin? {
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = responseToken.derivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
return CryptoCurrency.Coin(
|
||||
|
|
@ -106,11 +114,13 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
|
|||
sdkToken: Token,
|
||||
responseDerivationPath: String?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): CryptoCurrency.Token? {
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = responseDerivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
package com.tangem.data.common.currency
|
||||
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.common.tokens.getDefaultWalletBlockchains
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
class UserTokensResponseFactory @Inject constructor() {
|
||||
|
||||
fun createUserTokensResponse(
|
||||
|
|
@ -43,4 +49,38 @@ class UserTokensResponseFactory @Inject constructor() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun createDefaultResponse(
|
||||
userWallet: UserWallet?,
|
||||
networkFactory: NetworkFactory,
|
||||
accountId: AccountId?,
|
||||
): UserTokensResponse {
|
||||
val tokens = userWallet?.let {
|
||||
getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig())
|
||||
.map { blockchain ->
|
||||
val derivationPath = networkFactory.createDerivationPath(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
cardDerivationStyleProvider = userWallet.derivationStyleProvider,
|
||||
).value
|
||||
|
||||
UserTokensResponse.Token(
|
||||
id = blockchain.toCoinId(),
|
||||
accountId = accountId?.value,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
derivationPath = derivationPath,
|
||||
name = blockchain.getCoinName(),
|
||||
symbol = blockchain.currency,
|
||||
decimals = blockchain.decimals(),
|
||||
contractAddress = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return UserTokensResponse(
|
||||
group = UserTokensResponse.GroupType.NONE,
|
||||
sort = UserTokensResponse.SortType.MANUAL,
|
||||
tokens = tokens.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.common.di
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.cache.etag.DefaultETagsStore
|
||||
import com.tangem.data.common.cache.etag.ETagsStore
|
||||
import com.tangem.data.common.currency.*
|
||||
|
|
@ -30,6 +31,8 @@ internal object DataCommonModule {
|
|||
fun provideCardCryptoCurrencyFactory(
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
accountsFeatureToggles: AccountsFeatureToggles,
|
||||
walletAccountsFetcher: WalletAccountsFetcher,
|
||||
userTokensResponseStore: UserTokensResponseStore,
|
||||
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
|
||||
): CardCryptoCurrencyFactory {
|
||||
|
|
@ -37,6 +40,8 @@ internal object DataCommonModule {
|
|||
demoConfig = DemoConfig(),
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
userWalletsStore = userWalletsStore,
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
walletAccountsFetcher = walletAccountsFetcher,
|
||||
userTokensResponseStore = userTokensResponseStore,
|
||||
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.domain.card.common.extensions.canHandleToken
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.lib.crypto.derivation.toMutable
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -21,6 +23,7 @@ import javax.inject.Inject
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("LargeClass")
|
||||
class NetworkFactory @Inject constructor(
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
|
|
@ -32,13 +35,19 @@ class NetworkFactory @Inject constructor(
|
|||
* @param extraDerivationPath extra derivation path
|
||||
* @param userWallet user wallet
|
||||
*/
|
||||
fun create(blockchain: Blockchain, extraDerivationPath: String?, userWallet: UserWallet): Network? {
|
||||
fun create(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): Network? {
|
||||
return create(
|
||||
blockchain = blockchain,
|
||||
derivationPath = createDerivationPath(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
cardDerivationStyleProvider = userWallet.derivationStyleProvider,
|
||||
accountIndex = accountIndex,
|
||||
),
|
||||
canHandleTokens = userWallet.canHandleToken(
|
||||
blockchain = blockchain,
|
||||
|
|
@ -80,6 +89,7 @@ class NetworkFactory @Inject constructor(
|
|||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
canHandleTokens: Boolean,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): Network? {
|
||||
return create(
|
||||
blockchain = blockchain,
|
||||
|
|
@ -87,6 +97,7 @@ class NetworkFactory @Inject constructor(
|
|||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
cardDerivationStyleProvider = derivationStyleProvider,
|
||||
accountIndex = accountIndex,
|
||||
),
|
||||
canHandleTokens = canHandleTokens,
|
||||
)
|
||||
|
|
@ -130,14 +141,15 @@ class NetworkFactory @Inject constructor(
|
|||
return true
|
||||
}
|
||||
|
||||
private fun createDerivationPath(
|
||||
fun createDerivationPath(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
cardDerivationStyleProvider: DerivationStyleProvider?,
|
||||
accountIndex: DerivationIndex? = null,
|
||||
): Network.DerivationPath {
|
||||
if (cardDerivationStyleProvider == null) return Network.DerivationPath.None
|
||||
|
||||
val defaultDerivationPath = getDefaultDerivationPath(blockchain, cardDerivationStyleProvider)
|
||||
val defaultDerivationPath = getDefaultDerivationPath(blockchain, cardDerivationStyleProvider, accountIndex)
|
||||
|
||||
return if (extraDerivationPath.isNullOrBlank()) {
|
||||
if (defaultDerivationPath.isNullOrBlank()) {
|
||||
|
|
@ -146,10 +158,11 @@ class NetworkFactory @Inject constructor(
|
|||
Network.DerivationPath.Card(defaultDerivationPath)
|
||||
}
|
||||
} else {
|
||||
if (extraDerivationPath == defaultDerivationPath) {
|
||||
Network.DerivationPath.Card(defaultDerivationPath)
|
||||
} else {
|
||||
val isMainIndexOrNull = accountIndex == null || accountIndex == DerivationIndex.Main
|
||||
if (extraDerivationPath != defaultDerivationPath && isMainIndexOrNull) {
|
||||
Network.DerivationPath.Custom(extraDerivationPath)
|
||||
} else {
|
||||
Network.DerivationPath.Card(extraDerivationPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,8 +170,19 @@ class NetworkFactory @Inject constructor(
|
|||
private fun getDefaultDerivationPath(
|
||||
blockchain: Blockchain,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
accountIndex: DerivationIndex?,
|
||||
): String? {
|
||||
return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath
|
||||
val default = blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())
|
||||
?: return null
|
||||
|
||||
return if (accountIndex == null || accountIndex == DerivationIndex.Main) {
|
||||
default
|
||||
} else {
|
||||
default.toMutable()
|
||||
.replaceAccountNode(value = accountIndex.value.toLong(), blockchain = blockchain)
|
||||
.apply()
|
||||
}
|
||||
.rawPath
|
||||
}
|
||||
|
||||
private fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType {
|
||||
|
|
@ -326,8 +350,8 @@ class NetworkFactory @Inject constructor(
|
|||
Blockchain.Pepecoin, Blockchain.PepecoinTestnet,
|
||||
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet,
|
||||
Blockchain.Quai, Blockchain.QuaiTestnet,
|
||||
// Blockchain.Linea, Blockchain.LineaTestnet,
|
||||
// Blockchain.ArbitrumNova,
|
||||
Blockchain.Linea, Blockchain.LineaTestnet,
|
||||
Blockchain.ArbitrumNova,
|
||||
-> Network.TransactionExtrasType.NONE
|
||||
// endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.data.common.tokens
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
/**
|
||||
* Returns the default blockchains for the multi-currency wallet.
|
||||
*
|
||||
* @param userWallet The user's wallet, which can be either a cold or hot wallet.
|
||||
* @param demoConfig Configuration for demo cards, which may specify different default blockchains.
|
||||
*/
|
||||
fun getDefaultWalletBlockchains(userWallet: UserWallet, demoConfig: DemoConfig): Collection<Blockchain> {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
val card = userWallet.scanResponse.card
|
||||
|
||||
var blockchainsInternal = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
demoConfig.demoBlockchains
|
||||
} else {
|
||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
|
||||
if (card.isTestCard) {
|
||||
blockchainsInternal = blockchainsInternal.mapNotNull { it.getTestnetVersion() }
|
||||
}
|
||||
|
||||
blockchainsInternal
|
||||
}
|
||||
is UserWallet.Hot -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ import com.tangem.common.test.domain.card.MockScanResponseFactory
|
|||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.card.configs.GenericCardConfig
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
|
|
@ -37,6 +39,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
|||
private val userWalletsStore: UserWalletsStore = mockk()
|
||||
private val userTokensResponseStore: UserTokensResponseStore = mockk()
|
||||
private val excludedBlockchains = ExcludedBlockchains()
|
||||
private val accountsFeatureToggles = mockk<AccountsFeatureToggles>()
|
||||
private val walletAccountsFetcher = mockk<WalletAccountsFetcher>()
|
||||
|
||||
private val factory = DefaultCardCryptoCurrencyFactory(
|
||||
demoConfig = DemoConfig(),
|
||||
|
|
@ -46,6 +50,8 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
|||
responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(
|
||||
networkFactory = NetworkFactory(excludedBlockchains = excludedBlockchains),
|
||||
),
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
walletAccountsFetcher = walletAccountsFetcher,
|
||||
)
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
|
|
@ -57,7 +63,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
|||
|
||||
@BeforeEach
|
||||
fun init() {
|
||||
clearMocks(userWalletsStore, userTokensResponseStore, iconUri)
|
||||
clearMocks(userWalletsStore, userTokensResponseStore, accountsFeatureToggles, walletAccountsFetcher, iconUri)
|
||||
|
||||
mockkStatic(Uri::class)
|
||||
every { Uri.parse(any()) } returns iconUri
|
||||
|
|
@ -75,6 +81,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
|||
val userTokensResponse = model.userTokensResponse
|
||||
val network = ethereum.network
|
||||
|
||||
every { accountsFeatureToggles.isFeatureEnabled } returns false
|
||||
coEvery { userWalletsStore.getSyncStrict(key = userWallet.walletId) } returns userWallet
|
||||
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
||||
|
||||
|
|
@ -236,6 +243,7 @@ internal class DefaultCardCryptoCurrencyFactoryTest {
|
|||
val networks = setOf(ethereum.network, bitcoin.network)
|
||||
val userTokensResponse = model.userTokensResponse
|
||||
|
||||
every { accountsFeatureToggles.isFeatureEnabled } returns false
|
||||
coEvery { userTokensResponseStore.getSyncOrNull(userWallet.walletId) } returns userTokensResponse
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
package com.tangem.data.express
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.data.express.converter.ExpressAssetConverter
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressUtils.getRefCode
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
typealias InitializationStatusFlow = MutableStateFlow<Lce<Throwable, List<ExpressAsset>>>
|
||||
|
||||
/**
|
||||
* Default implementation of [ExpressServiceFetcher]
|
||||
*
|
||||
* @property tangemExpressApi express api
|
||||
* @property expressAssetsStore local storage
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultExpressServiceFetcher @Inject constructor(
|
||||
private val tangemExpressApi: TangemExpressApi,
|
||||
private val expressAssetsStore: ExpressAssetsStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ExpressServiceFetcher {
|
||||
|
||||
private val initializationStatuses =
|
||||
MutableStateFlow<Map<UserWalletId, InitializationStatusFlow>>(value = emptyMap())
|
||||
|
||||
override suspend fun fetch(userWalletId: UserWalletId, assetIds: Set<ExpressAsset.ID>): Either<Throwable, Unit> =
|
||||
either {
|
||||
val userWallet = arrow.core.raise.catch(
|
||||
block = { userWalletsStore.getSyncStrict(userWalletId) },
|
||||
catch = ::raise,
|
||||
)
|
||||
|
||||
fetch(userWallet = userWallet, assetIds = assetIds).bind()
|
||||
}
|
||||
|
||||
override suspend fun fetch(userWallet: UserWallet, assetIds: Set<ExpressAsset.ID>): Either<Throwable, Unit> {
|
||||
return Either.catchOn(dispatchers.io) {
|
||||
val initializationStatus = getInitializationStatusInternal(userWallet.walletId)
|
||||
|
||||
try {
|
||||
if (assetIds.isNotEmpty()) {
|
||||
val tokenList = assetIds.map {
|
||||
LeastTokenInfo(contractAddress = it.contractAddress, network = it.networkId)
|
||||
}
|
||||
|
||||
val response = tangemExpressApi.getAssets(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
refCode = getRefCode(userWallet, appPreferencesStore),
|
||||
body = AssetsRequestBody(tokensList = tokenList),
|
||||
).getOrThrow()
|
||||
|
||||
expressAssetsStore.store(userWallet.walletId, response)
|
||||
|
||||
val expressAssets = ExpressAssetConverter.convertList(response)
|
||||
initializationStatus.update { expressAssets.lceContent() }
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
if (expressAssetsStore.getSyncOrNull(userWallet.walletId) == null) {
|
||||
initializationStatus.update { e.lceError() }
|
||||
}
|
||||
Timber.e(e, "Unable to fetch assets for: ${userWallet.walletId.stringValue}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<ExpressAsset>>> {
|
||||
return flow { getInitializationStatusInternal(userWalletId).collect { emit(it) } }
|
||||
}
|
||||
|
||||
@Suppress("SuspendFunWithFlowReturnType")
|
||||
private suspend fun getInitializationStatusInternal(userWalletId: UserWalletId): InitializationStatusFlow {
|
||||
val initializationStatus = initializationStatuses.value[userWalletId]
|
||||
if (initializationStatus != null) return initializationStatus
|
||||
|
||||
val cached = expressAssetsStore.getSyncOrNull(userWalletId)?.let(ExpressAssetConverter::convertList)
|
||||
val default: InitializationStatusFlow = MutableStateFlow(value = cached?.lceContent() ?: lceLoading())
|
||||
|
||||
initializationStatuses.update { statuses ->
|
||||
statuses.toMutableMap().apply {
|
||||
put(key = userWalletId, value = default)
|
||||
}
|
||||
}
|
||||
|
||||
return default
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.data.express.converter
|
||||
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converts an [Asset] from the data layer to an [ExpressAsset] in the domain layer.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object ExpressAssetConverter : Converter<Asset, ExpressAsset> {
|
||||
|
||||
override fun convert(value: Asset): ExpressAsset {
|
||||
return ExpressAsset(
|
||||
id = ExpressAsset.ID(
|
||||
networkId = value.network,
|
||||
contractAddress = value.contractAddress,
|
||||
),
|
||||
isExchangeAvailable = value.exchangeAvailable,
|
||||
isOnrampAvailable = value.onrampAvailable,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.express.di
|
|||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.express.DefaultExpressRepository
|
||||
import com.tangem.data.express.DefaultExpressServiceFetcher
|
||||
import com.tangem.data.express.converter.ExpressErrorConverter
|
||||
import com.tangem.data.express.error.DefaultExpressErrorResolver
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.datasource.di.NetworkMoshi
|
|||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.express.ExpressErrorResolver
|
||||
import com.tangem.domain.express.ExpressRepository
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -43,4 +45,10 @@ internal object ExpressDataModule {
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideExpressServiceFetcher(impl: DefaultExpressServiceFetcher): ExpressServiceFetcher {
|
||||
return impl
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ android {
|
|||
dependencies {
|
||||
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.demo)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.manageTokens)
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ internal class DefaultCustomTokensRepository(
|
|||
"User tokens not found for user wallet [$userWalletId] while removing currency"
|
||||
}
|
||||
|
||||
val token = userTokensResponseFactory.createResponseToken(cryptoCurrency)
|
||||
val token = userTokensResponseFactory.createResponseToken(currency = cryptoCurrency, accountId = null)
|
||||
userTokensSaver.storeAndPush(
|
||||
userWalletId = userWalletId,
|
||||
response = storedCurrencies.copy(tokens = storedCurrencies.tokens.filterNot { it == token }),
|
||||
|
|
@ -249,6 +249,27 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun convertToCryptoCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
currency: ManagedCryptoCurrency.Custom,
|
||||
): CryptoCurrency {
|
||||
return when (currency) {
|
||||
is ManagedCryptoCurrency.Custom.Coin -> createCoin(
|
||||
userWalletId = userWalletId,
|
||||
networkId = currency.network.id,
|
||||
derivationPath = currency.network.derivationPath,
|
||||
)
|
||||
is ManagedCryptoCurrency.Custom.Token -> cryptoCurrencyFactory.createToken(
|
||||
network = currency.network,
|
||||
rawId = currency.currencyId.rawCurrencyId,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
decimals = currency.decimals,
|
||||
contractAddress = currency.contractAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSupportedNetworks(userWalletId: UserWalletId): List<Network> = withContext(dispatchers.io) {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"User wallet [$userWalletId] not found while getting supported networks"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
|
|
@ -16,20 +17,20 @@ import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
|||
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.card.common.extensions.canHandleBlockchain
|
||||
import com.tangem.domain.card.common.extensions.canHandleToken
|
||||
import com.tangem.domain.card.common.extensions.hotWalletExcludedBlockchains
|
||||
import com.tangem.domain.card.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.card.common.extensions.supportedTokens
|
||||
import com.tangem.domain.card.common.extensions.*
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.managetokens.model.*
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -40,7 +41,7 @@ import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request
|
|||
import com.tangem.pagination.toBatchFlow
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class DefaultManageTokensRepository(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
|
|
@ -51,10 +52,16 @@ internal class DefaultManageTokensRepository(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
networkFactory: NetworkFactory,
|
||||
) : ManageTokensRepository {
|
||||
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(networkFactory, excludedBlockchains)
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(
|
||||
networkFactory = networkFactory,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
// region getTokenListBatchFlow
|
||||
|
|
@ -82,7 +89,10 @@ internal class DefaultManageTokensRepository(
|
|||
val userWallet = request.params.userWalletId?.let(userWalletsStore::getSyncStrict)
|
||||
|
||||
if (userWallet is UserWallet.Cold && userWallet.scanResponse.card.isTestCard) {
|
||||
fetchTestnetCurrencies(userWallet, request)
|
||||
when (val params = request.params) {
|
||||
is ManageTokensListConfig.Account -> fetchTestnetCurrencies(userWallet, params)
|
||||
is ManageTokensListConfig.Wallet -> fetchTestnetCurrenciesLegacy(userWallet, params)
|
||||
}
|
||||
} else {
|
||||
fetchCurrencies(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -94,7 +104,6 @@ internal class DefaultManageTokensRepository(
|
|||
},
|
||||
)
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private suspend fun fetchCurrencies(
|
||||
userWallet: UserWallet?,
|
||||
request: Request<ManageTokensListConfig>,
|
||||
|
|
@ -127,31 +136,22 @@ internal class DefaultManageTokensRepository(
|
|||
coins = coinsResponse.coins.filterNot { l2BlockchainsCoinIds.contains(it.id) },
|
||||
)
|
||||
|
||||
val tokensResponse = request.params.userWalletId?.let { userWalletId ->
|
||||
if (loadUserTokensFromRemote && userWallet != null) {
|
||||
safeApiCall({ tangemTechApi.getUserTokens(userWalletId.stringValue).bind() }) {
|
||||
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
|
||||
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
|
||||
}
|
||||
} else {
|
||||
getSavedUserTokensResponseSync(userWalletId)
|
||||
}
|
||||
}
|
||||
val items = if (isFirstBatchFetching &&
|
||||
tokensResponse != null &&
|
||||
userWallet != null &&
|
||||
query == null
|
||||
) {
|
||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
val items = when (val params = request.params) {
|
||||
is ManageTokensListConfig.Account -> createManagedCryptoCurrencyList(
|
||||
params = params,
|
||||
userWallet = userWallet,
|
||||
isFirstBatchFetching = isFirstBatchFetching,
|
||||
loadUserTokensFromRemote = loadUserTokensFromRemote,
|
||||
query = query,
|
||||
updatedCoinsResponse = updatedCoinsResponse,
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
is ManageTokensListConfig.Wallet -> createManagedCryptoCurrencyListLegacy(
|
||||
params = params,
|
||||
userWallet = userWallet,
|
||||
isFirstBatchFetching = isFirstBatchFetching,
|
||||
loadUserTokensFromRemote = loadUserTokensFromRemote,
|
||||
query = query,
|
||||
updatedCoinsResponse = updatedCoinsResponse,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +162,112 @@ internal class DefaultManageTokensRepository(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun createManagedCryptoCurrencyList(
|
||||
params: ManageTokensListConfig.Account,
|
||||
userWallet: UserWallet?,
|
||||
isFirstBatchFetching: Boolean,
|
||||
loadUserTokensFromRemote: Boolean,
|
||||
query: String?,
|
||||
updatedCoinsResponse: CoinsResponse,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val response = params.userWalletId?.let { userWalletId ->
|
||||
if (loadUserTokensFromRemote && userWallet != null) {
|
||||
runCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
|
||||
} else {
|
||||
walletAccountsFetcher.getSaved(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
val accountId = when {
|
||||
params.accountId == null -> null
|
||||
loadUserTokensFromRemote -> {
|
||||
AccountId.forCryptoPortfolio(
|
||||
userWalletId = requireNotNull(params.accountId).userWalletId,
|
||||
derivationIndex = DerivationIndex.Main,
|
||||
)
|
||||
}
|
||||
else -> requireNotNull(params.accountId)
|
||||
}
|
||||
|
||||
val accountDTO = if (response != null && accountId != null) {
|
||||
response.accounts.firstOrNull { it.id == accountId.value }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val tokensResponse = response?.let {
|
||||
UserTokensResponse(
|
||||
group = response.wallet.group,
|
||||
sort = response.wallet.sort,
|
||||
tokens = accountDTO?.tokens.orEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
val isCreateWithCustom = isFirstBatchFetching &&
|
||||
tokensResponse != null &&
|
||||
userWallet != null &&
|
||||
query == null
|
||||
|
||||
val items = if (isCreateWithCustom) {
|
||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
|
||||
)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
private suspend fun createManagedCryptoCurrencyListLegacy(
|
||||
params: ManageTokensListConfig.Wallet,
|
||||
userWallet: UserWallet?,
|
||||
isFirstBatchFetching: Boolean,
|
||||
loadUserTokensFromRemote: Boolean,
|
||||
query: String?,
|
||||
updatedCoinsResponse: CoinsResponse,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val tokensResponse = params.userWalletId?.let { userWalletId ->
|
||||
if (loadUserTokensFromRemote && userWallet != null) {
|
||||
safeApiCall({ tangemTechApi.getUserTokens(userWalletId.stringValue).bind() }) {
|
||||
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
|
||||
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
|
||||
}
|
||||
} else {
|
||||
getSavedUserTokensResponseSync(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
val isCreateWithCustom = isFirstBatchFetching &&
|
||||
tokensResponse != null &&
|
||||
userWallet != null &&
|
||||
query == null
|
||||
|
||||
return if (isCreateWithCustom) {
|
||||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
userWallet = userWallet,
|
||||
accountIndex = null,
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
userWallet = userWallet,
|
||||
accountIndex = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createAndSaveDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
||||
val userTokensResponse = createDefaultUserTokensResponse(userWallet)
|
||||
userTokenSaver.store(userWallet.walletId, userTokensResponse, useEnricher = false)
|
||||
|
|
@ -170,9 +276,59 @@ internal class DefaultManageTokensRepository(
|
|||
|
||||
private suspend fun fetchTestnetCurrencies(
|
||||
userWallet: UserWallet,
|
||||
request: Request<ManageTokensListConfig>,
|
||||
params: ManageTokensListConfig.Account,
|
||||
): BatchFetchResult.Success<List<ManagedCryptoCurrency>> {
|
||||
val searchText = request.params.searchText
|
||||
val searchText = params.searchText
|
||||
val testnetTokensConfig = testnetTokensStorage.getConfig()
|
||||
|
||||
val response = params.userWalletId?.let { userWalletId ->
|
||||
walletAccountsFetcher.getSaved(userWalletId)
|
||||
}
|
||||
|
||||
val accountId = params.accountId
|
||||
|
||||
val accountDTO = if (response != null && accountId != null) {
|
||||
response.accounts.firstOrNull { it.id == accountId.value }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val tokensResponse = response?.let {
|
||||
UserTokensResponse(
|
||||
group = response.wallet.group,
|
||||
sort = response.wallet.sort,
|
||||
tokens = accountDTO?.tokens.orEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
val items = managedCryptoCurrencyFactory.createTestnetWithCustomTokens(
|
||||
testnetTokensConfig = if (!searchText.isNullOrBlank()) {
|
||||
testnetTokensConfig.copy(
|
||||
tokens = testnetTokensConfig.tokens.filter { token ->
|
||||
token.symbol.contains(other = searchText, ignoreCase = true) ||
|
||||
token.name.contains(other = searchText, ignoreCase = true)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
testnetTokensConfig
|
||||
},
|
||||
tokensResponse = tokensResponse,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
|
||||
)
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
data = items,
|
||||
empty = items.isEmpty(),
|
||||
last = true,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchTestnetCurrenciesLegacy(
|
||||
userWallet: UserWallet,
|
||||
params: ManageTokensListConfig.Wallet,
|
||||
): BatchFetchResult.Success<List<ManagedCryptoCurrency>> {
|
||||
val searchText = params.searchText
|
||||
val testnetTokensConfig = testnetTokensStorage.getConfig()
|
||||
|
||||
val items = managedCryptoCurrencyFactory.createTestnetWithCustomTokens(
|
||||
|
|
@ -188,6 +344,7 @@ internal class DefaultManageTokensRepository(
|
|||
},
|
||||
tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId),
|
||||
userWallet = userWallet,
|
||||
accountIndex = null,
|
||||
)
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.managetokens.di
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
|
|
@ -11,6 +12,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
|
|||
import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
|
@ -38,6 +40,8 @@ internal object ManageTokensDataModule {
|
|||
excludedBlockchains: ExcludedBlockchains,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
networkFactory: NetworkFactory,
|
||||
accountsFeatureToggles: AccountsFeatureToggles,
|
||||
walletAccountsFetcher: WalletAccountsFetcher,
|
||||
): ManageTokensRepository {
|
||||
return DefaultManageTokensRepository(
|
||||
tangemTechApi = tangemTechApi,
|
||||
|
|
@ -50,6 +54,8 @@ internal object ManageTokensDataModule {
|
|||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
networkFactory = networkFactory,
|
||||
dispatchers = dispatchers,
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
walletAccountsFetcher = walletAccountsFetcher,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ import com.tangem.data.common.network.NetworkFactory
|
|||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.card.common.extensions.canHandleToken
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -26,15 +28,17 @@ import timber.log.Timber
|
|||
internal class ManagedCryptoCurrencyFactory(
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
) {
|
||||
|
||||
fun create(
|
||||
coinsResponse: CoinsResponse,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
userWallet: UserWallet?,
|
||||
accountIndex: DerivationIndex?,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
return coinsResponse.coins.mapNotNull { coin ->
|
||||
createToken(coin, tokensResponse, coinsResponse.imageHost, userWallet)
|
||||
createToken(coin, tokensResponse, coinsResponse.imageHost, userWallet, accountIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,9 +46,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
coinsResponse: CoinsResponse,
|
||||
tokensResponse: UserTokensResponse,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = createCustomTokens(tokensResponse, userWallet)
|
||||
val tokens = create(coinsResponse, tokensResponse, userWallet)
|
||||
val customTokens = createCustomTokens(tokensResponse, userWallet, accountIndex)
|
||||
val tokens = create(coinsResponse, tokensResponse, userWallet, accountIndex)
|
||||
|
||||
return customTokens + tokens
|
||||
}
|
||||
|
|
@ -53,9 +58,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
testnetTokensConfig: TestnetTokensConfig,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = tokensResponse
|
||||
?.let { createCustomTokens(it, userWallet) }
|
||||
?.let { createCustomTokens(it, userWallet, accountIndex) }
|
||||
?: emptyList()
|
||||
val testnetTokens = testnetTokensConfig.tokens.map { testnetToken ->
|
||||
ManagedCryptoCurrency.Token(
|
||||
|
|
@ -69,9 +75,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
contractAddress = network.address,
|
||||
decimals = network.decimalCount,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
} ?: emptyList(),
|
||||
addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, userWallet),
|
||||
addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, userWallet, accountIndex),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -81,28 +88,47 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
private fun createCustomTokens(
|
||||
tokensResponse: UserTokensResponse,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): List<ManagedCryptoCurrency> = tokensResponse.tokens
|
||||
.mapNotNull { token ->
|
||||
maybeCreateCustomToken(token, userWallet)
|
||||
maybeCreateCustomToken(token, userWallet, accountIndex)
|
||||
}
|
||||
|
||||
private fun maybeCreateCustomToken(
|
||||
token: UserTokensResponse.Token,
|
||||
userWallet: UserWallet,
|
||||
accountIndex: DerivationIndex?,
|
||||
): ManagedCryptoCurrency? {
|
||||
val blockchain = Blockchain.fromNetworkId(token.networkId)
|
||||
?.takeUnless { it in excludedBlockchains }
|
||||
?: return null
|
||||
|
||||
if (!checkIsCustomToken(token, blockchain, userWallet.derivationStyleProvider)) {
|
||||
return null
|
||||
val network = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
if (!checkIsCustomToken(token, network.derivationPath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
network
|
||||
} else {
|
||||
if (!checkIsCustomToken(token, blockchain, userWallet.derivationStyleProvider)) {
|
||||
return null
|
||||
}
|
||||
|
||||
networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
}
|
||||
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
userWallet = userWallet,
|
||||
) ?: return null
|
||||
val contractAddress = token.contractAddress
|
||||
|
||||
return if (contractAddress.isNullOrBlank()) {
|
||||
|
|
@ -135,6 +161,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
tokensResponse: UserTokensResponse?,
|
||||
imageHost: String?,
|
||||
userWallet: UserWallet?,
|
||||
accountIndex: DerivationIndex?,
|
||||
): ManagedCryptoCurrency? {
|
||||
if (coinResponse.networks.isEmpty() || !coinResponse.active) return null
|
||||
|
||||
|
|
@ -146,6 +173,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
contractAddress = network.contractAddress,
|
||||
decimals = network.decimalCount?.toInt(),
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
}
|
||||
.ifEmpty { return null }
|
||||
|
|
@ -156,7 +184,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
symbol = coinResponse.symbol,
|
||||
iconUrl = getIconUrl(coinResponse.id, imageHost),
|
||||
availableNetworks = availableNetworks,
|
||||
addedIn = findAddedInNetworks(coinResponse.id, tokensResponse, userWallet),
|
||||
addedIn = findAddedInNetworks(coinResponse.id, tokensResponse, userWallet, accountIndex),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +194,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
decimals: Int?,
|
||||
userWallet: UserWallet?,
|
||||
extraDerivationPath: String? = null,
|
||||
accountIndex: DerivationIndex?,
|
||||
): SourceNetwork? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId)
|
||||
?.takeUnless { it in excludedBlockchains }
|
||||
|
|
@ -176,6 +205,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
extraDerivationPath = extraDerivationPath,
|
||||
derivationStyleProvider = userWallet?.derivationStyleProvider,
|
||||
canHandleTokens = userWallet?.canHandleToken(blockchain, excludedBlockchains) == true,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
return when {
|
||||
|
|
@ -205,6 +235,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
currencyId: String,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
userWallet: UserWallet?,
|
||||
accountIndex: DerivationIndex?,
|
||||
): Set<Network> {
|
||||
if (tokensResponse == null) return emptySet()
|
||||
|
||||
|
|
@ -219,6 +250,7 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
extraDerivationPath = token.derivationPath,
|
||||
derivationStyleProvider = userWallet?.derivationStyleProvider,
|
||||
canHandleTokens = userWallet?.canHandleToken(blockchain, excludedBlockchains) != false,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -237,6 +269,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
): Boolean = token.id.isNullOrBlank() ||
|
||||
checkIsCustomDerivationPath(token.derivationPath, blockchain, derivationStyleProvider)
|
||||
|
||||
private fun checkIsCustomToken(token: UserTokensResponse.Token, derivationPath: Network.DerivationPath): Boolean {
|
||||
return token.id.isNullOrBlank() || derivationPath is Network.DerivationPath.Custom
|
||||
}
|
||||
|
||||
private fun checkIsCustomDerivationPath(
|
||||
derivationPath: String?,
|
||||
blockchain: Blockchain,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.datasource.local.datastore.RuntimeStateStore
|
|||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.pagination.*
|
||||
|
|
@ -250,6 +251,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
userWalletId: UserWalletId,
|
||||
token: TokenMarketParams,
|
||||
network: TokenMarketInfo.Network,
|
||||
accountIndex: DerivationIndex?,
|
||||
): CryptoCurrency? {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("UserWalletId [$userWalletId] not found")
|
||||
val blockchain = Blockchain.fromNetworkId(network.networkId) ?: error("Unknown network [${network.networkId}]")
|
||||
|
|
@ -259,12 +261,14 @@ internal class DefaultMarketsTokenRepository(
|
|||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
} else {
|
||||
val currencyNetwork = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return null
|
||||
|
||||
cryptoCurrencyFactory.createToken(
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ dependencies {
|
|||
implementation(projects.domain.walletManager)
|
||||
implementation(projects.domain.appTheme.models)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.express.models)
|
||||
|
||||
// region DI
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.data.onramp.converters.error.OnrampErrorConverter
|
|||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues
|
||||
import com.tangem.datasource.api.express.models.response.ExchangeProvider
|
||||
import com.tangem.datasource.api.express.models.response.ExchangeProviderType
|
||||
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
|
||||
|
|
@ -38,6 +37,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
|
|||
import com.tangem.datasource.local.preferences.utils.getObject
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.preferences.utils.storeObject
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -560,7 +560,7 @@ internal class DefaultOnrampRepository(
|
|||
}
|
||||
|
||||
private fun CryptoCurrency.getContractAddress(): String = when (this) {
|
||||
is CryptoCurrency.Coin -> TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
is CryptoCurrency.Coin -> ExpressAsset.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
is CryptoCurrency.Token -> this.contractAddress
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ internal class HotCryptoCurrencyConverter(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO account
|
||||
private fun createNetwork(networkId: String): Network? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId) ?: return null
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ public val Blockchain.mercuryoNetwork: String?
|
|||
Blockchain.Pepecoin, Blockchain.PepecoinTestnet -> null
|
||||
Blockchain.Hyperliquid, Blockchain.HyperliquidTestnet -> null
|
||||
Blockchain.Quai, Blockchain.QuaiTestnet -> null
|
||||
// Blockchain.Linea, Blockchain.LineaTestnet -> null
|
||||
// Blockchain.ArbitrumNova -> null
|
||||
Blockchain.Linea, Blockchain.LineaTestnet -> null
|
||||
Blockchain.ArbitrumNova -> null
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ dependencies {
|
|||
implementation(projects.domain.card)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.demo)
|
||||
implementation(projects.domain.express)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.staking)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.data.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.datasource.api.tangemTech.models.account.flattenTokens
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||
|
|
@ -15,6 +19,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
*
|
||||
* @property userWalletsStore [UserWallet]'s store
|
||||
* @property walletAccountsFetcher instance of [WalletAccountsFetcher] to fetch accounts for a multi wallet
|
||||
* @property expressServiceFetcher fetcher of express service
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -22,14 +27,26 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
internal class AccountListCryptoCurrenciesFetcher(
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val walletAccountsFetcher: WalletAccountsFetcher,
|
||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MultiWalletCryptoCurrenciesFetcher {
|
||||
|
||||
override suspend fun invoke(params: Params) = Either.catchOn(dispatchers.default) {
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
|
||||
override suspend fun invoke(params: Params): Either<Throwable, Unit> {
|
||||
return Either.catchOn(dispatchers.default) {
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
|
||||
|
||||
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
|
||||
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
|
||||
|
||||
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
||||
val response = walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
||||
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = userWallet,
|
||||
assetIds = response.flattenTokens().mapTo(hashSetOf()) {
|
||||
ExpressAsset.ID(networkId = it.networkId, contractAddress = it.contractAddress)
|
||||
},
|
||||
)
|
||||
|
||||
Unit.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,15 +7,14 @@ import com.tangem.data.common.currency.UserTokensResponseFactory
|
|||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||
|
|
@ -32,7 +31,7 @@ import timber.log.Timber
|
|||
* @property userTokensResponseStore store of [UserTokensResponse]
|
||||
* @property userTokensSaver user tokens saver
|
||||
* @property cardCryptoCurrencyFactory factory for creating crypto currencies for specified card
|
||||
* @property expressServiceLoader express service loader
|
||||
* @property expressServiceFetcher express service loader
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -46,7 +45,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
|||
private val userTokensResponseStore: UserTokensResponseStore,
|
||||
private val userTokensSaver: UserTokensSaver,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val expressServiceLoader: ExpressServiceLoader,
|
||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MultiWalletCryptoCurrenciesFetcher {
|
||||
|
||||
|
|
@ -109,14 +108,14 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
|||
}
|
||||
|
||||
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
||||
val tokens = userTokens.tokens.map { token ->
|
||||
LeastTokenInfo(
|
||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
||||
network = token.networkId,
|
||||
val tokens = userTokens.tokens.mapTo(hashSetOf()) { token ->
|
||||
ExpressAsset.ID(
|
||||
networkId = token.networkId,
|
||||
contractAddress = token.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
expressServiceLoader.update(userWallet = userWallet, userTokens = tokens)
|
||||
expressServiceFetcher.fetch(userWallet = userWallet, assetIds = tokens)
|
||||
}
|
||||
|
||||
private fun createDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
|
||||
|
|
@ -124,6 +123,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
|
|||
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
accountId = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,11 @@ import com.tangem.data.tokens.AccountListCryptoCurrenciesFetcher
|
|||
import com.tangem.data.tokens.DefaultMultiWalletCryptoCurrenciesFetcher
|
||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -33,7 +33,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
|||
userTokensResponseStore: UserTokensResponseStore,
|
||||
userTokensSaver: UserTokensSaver,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
expressServiceLoader: ExpressServiceLoader,
|
||||
expressServiceFetcher: ExpressServiceFetcher,
|
||||
walletAccountsFetcher: WalletAccountsFetcher,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): MultiWalletCryptoCurrenciesFetcher {
|
||||
|
|
@ -41,6 +41,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
|||
AccountListCryptoCurrenciesFetcher(
|
||||
userWalletsStore = userWalletsStore,
|
||||
walletAccountsFetcher = walletAccountsFetcher,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -56,7 +57,7 @@ internal class MultiWalletCryptoCurrenciesFetcherModule {
|
|||
userTokensResponseStore = userTokensResponseStore,
|
||||
userTokensSaver = userTokensSaver,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
expressServiceLoader = expressServiceLoader,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,14 @@ import com.tangem.data.common.cache.CacheRegistry
|
|||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.tokens.repository.DefaultCurrenciesRepository
|
||||
import com.tangem.data.tokens.repository.DefaultCurrencyChecksRepository
|
||||
import com.tangem.data.tokens.repository.DefaultPolkadotAccountHealthCheckRepository
|
||||
import com.tangem.data.tokens.repository.DefaultTokenReceiveWarningsViewedRepository
|
||||
import com.tangem.data.tokens.repository.DefaultYieldSupplyWarningsViewedRepository
|
||||
import com.tangem.data.tokens.repository.*
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.token.TokenReceiveWarningActionStore
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository
|
||||
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -42,7 +34,7 @@ internal object TokensDataModule {
|
|||
walletManagersFacade: WalletManagersFacade,
|
||||
cacheRegistry: CacheRegistry,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
expressServiceLoader: ExpressServiceLoader,
|
||||
expressServiceFetcher: ExpressServiceFetcher,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
tokensSaver: UserTokensSaver,
|
||||
|
|
@ -54,7 +46,7 @@ internal object TokensDataModule {
|
|||
walletManagersFacade = walletManagersFacade,
|
||||
cacheRegistry = cacheRegistry,
|
||||
userTokensResponseStore = userTokensResponseStore,
|
||||
expressServiceLoader = expressServiceLoader,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
dispatchers = dispatchers,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
|
|
|
|||
|
|
@ -10,17 +10,16 @@ import com.tangem.data.common.currency.*
|
|||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -40,7 +39,7 @@ internal class DefaultCurrenciesRepository(
|
|||
private val userWalletsStore: UserWalletsStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val expressServiceLoader: ExpressServiceLoader,
|
||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val userTokensSaver: UserTokensSaver,
|
||||
|
|
@ -74,26 +73,6 @@ internal class DefaultCurrenciesRepository(
|
|||
userTokensSaver.storeAndPush(userWalletId, response)
|
||||
}
|
||||
|
||||
override suspend fun saveCurrenciesLocal(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
withContext(dispatchers.io) {
|
||||
val savedResponse = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = { "Saved tokens empty. Can not perform add currencies action." },
|
||||
)
|
||||
|
||||
val updatedResponse = savedResponse.copy(
|
||||
tokens = currencies.map(userTokensResponseFactory::createResponseToken),
|
||||
)
|
||||
|
||||
userTokensSaver.store(userWalletId = userWalletId, response = updatedResponse)
|
||||
|
||||
fetchExpressAssetsByNetworkIds(
|
||||
userWallet = userWalletsStore.getSyncStrict(key = userWalletId),
|
||||
userTokens = updatedResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun addCurrenciesCache(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
|
|
@ -322,50 +301,6 @@ internal class DefaultCurrenciesRepository(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCachedCurrenciesSync(userWalletId: UserWalletId) =
|
||||
withContext(dispatchers.io) {
|
||||
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
|
||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
|
||||
val storedTokens = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWallet.walletId),
|
||||
lazyMessage = {
|
||||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
},
|
||||
)
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrencies(
|
||||
storedTokens,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
id: CryptoCurrency.ID,
|
||||
): CryptoCurrency = withContext(dispatchers.io) {
|
||||
getMultiCurrencyWalletCurrency(userWalletId, id.value)
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCurrency(userWalletId: UserWalletId, id: String): CryptoCurrency =
|
||||
withContext(dispatchers.io) {
|
||||
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
|
||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
|
||||
val response = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = {
|
||||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
},
|
||||
)
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrency(
|
||||
currencyId = id,
|
||||
response = response,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getNetworkCoin(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
|
|
@ -667,16 +602,17 @@ internal class DefaultCurrenciesRepository(
|
|||
return demoConfig.isDemoCardId(userWallet.cardId) && response == null
|
||||
}
|
||||
|
||||
// TODO [REDACTED_JIRA]
|
||||
private suspend fun fetchExpressAssetsByNetworkIds(userWallet: UserWallet, userTokens: UserTokensResponse) {
|
||||
val tokens = userTokens.tokens.map { token ->
|
||||
LeastTokenInfo(
|
||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
||||
network = token.networkId,
|
||||
val tokens = userTokens.tokens.mapTo(hashSetOf()) { token ->
|
||||
ExpressAsset.ID(
|
||||
networkId = token.networkId,
|
||||
contractAddress = token.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
launch { expressServiceLoader.update(userWallet, tokens) }
|
||||
launch { expressServiceFetcher.fetch(userWallet, tokens) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -685,11 +621,11 @@ internal class DefaultCurrenciesRepository(
|
|||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
refresh: Boolean = false,
|
||||
) {
|
||||
val tokens = cryptoCurrencies.map { currency ->
|
||||
val tokens = cryptoCurrencies.mapTo(hashSetOf()) { currency ->
|
||||
val tokenCurrency = currency as? CryptoCurrency.Token
|
||||
LeastTokenInfo(
|
||||
contractAddress = tokenCurrency?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
||||
network = currency.network.backendId,
|
||||
ExpressAsset.ID(
|
||||
networkId = currency.network.backendId,
|
||||
contractAddress = tokenCurrency?.contractAddress,
|
||||
)
|
||||
}
|
||||
cacheRegistry.invokeOnExpire(
|
||||
|
|
@ -697,7 +633,7 @@ internal class DefaultCurrenciesRepository(
|
|||
skipCache = refresh,
|
||||
block = {
|
||||
coroutineScope {
|
||||
launch { expressServiceLoader.update(userWallet, tokens) }
|
||||
launch { expressServiceFetcher.fetch(userWallet, tokens) }
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -731,6 +667,7 @@ internal class DefaultCurrenciesRepository(
|
|||
),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
accountId = null,
|
||||
)
|
||||
|
||||
private fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.data.tokens
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.common.test.utils.assertEither
|
||||
import com.tangem.common.test.utils.assertEitherRight
|
||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
|
|
@ -21,11 +24,13 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
|||
|
||||
private val userWalletsStore: UserWalletsStore = mockk(relaxUnitFun = true)
|
||||
private val walletAccountsFetcher: WalletAccountsFetcher = mockk(relaxUnitFun = true)
|
||||
private val expressServiceFetcher: ExpressServiceFetcher = mockk()
|
||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||
|
||||
private val fetcher = AccountListCryptoCurrenciesFetcher(
|
||||
userWalletsStore = userWalletsStore,
|
||||
walletAccountsFetcher = walletAccountsFetcher,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
|
|
@ -59,8 +64,11 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
|||
// Arrange
|
||||
val params = MultiWalletCryptoCurrenciesFetcher.Params(userWalletId = userWalletId)
|
||||
val mockUserWallet = mockk<UserWallet> { every { isMultiCurrency } returns true }
|
||||
val response = mockk<GetWalletAccountsResponse>(relaxed = true)
|
||||
|
||||
every { userWalletsStore.getSyncStrict(key = params.userWalletId) } returns mockUserWallet
|
||||
coEvery { walletAccountsFetcher.fetch(userWalletId = params.userWalletId) } returns response
|
||||
coEvery { expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = emptySet()) } returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
|
@ -71,6 +79,7 @@ internal class AccountListCryptoCurrenciesFetcherTest {
|
|||
coVerify(ordering = Ordering.SEQUENCE) {
|
||||
userWalletsStore.getSyncStrict(key = params.userWalletId)
|
||||
walletAccountsFetcher.fetch(userWalletId = params.userWalletId)
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = emptySet())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ import com.tangem.data.common.currency.UserTokensSaver
|
|||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
|
|
@ -45,7 +44,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true)
|
||||
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
||||
private val expressServiceLoader: ExpressServiceLoader = mockk(relaxUnitFun = true)
|
||||
private val expressServiceFetcher: ExpressServiceFetcher = mockk(relaxUnitFun = true)
|
||||
|
||||
private val fetcher = DefaultMultiWalletCryptoCurrenciesFetcher(
|
||||
demoConfig = DemoConfig(),
|
||||
|
|
@ -55,7 +54,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensResponseStore = userTokensResponseStore,
|
||||
userTokensSaver = userTokensSaver,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
expressServiceLoader = expressServiceLoader,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
|
|
@ -67,7 +66,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensResponseStore,
|
||||
userTokensSaver,
|
||||
cardCryptoCurrencyFactory,
|
||||
expressServiceLoader,
|
||||
expressServiceFetcher,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +130,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
} returns userTokensResponse
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = userTokensResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -144,7 +150,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +176,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
} returns apiResponse.data
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = defaultResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -183,7 +196,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
|
|
@ -212,6 +225,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
} returns apiResponse.data
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = apiResponse.data.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -224,7 +244,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = apiResponse.data)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
|
|
@ -273,6 +293,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
} returns userTokensResponse
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = userTokensResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -286,7 +313,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
|
|
@ -317,6 +344,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
} returns defaultResponse
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = defaultResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -330,7 +364,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
|
|
@ -383,6 +417,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
} returns userTokensResponse
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = userTokensResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -398,7 +439,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensSaver.push(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = userTokensResponse.toAssetId())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -429,6 +470,13 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
} returns defaultResponse
|
||||
|
||||
coEvery {
|
||||
expressServiceFetcher.fetch(
|
||||
userWallet = mockUserWallet,
|
||||
assetIds = defaultResponse.toAssetId(),
|
||||
)
|
||||
} returns Unit.right()
|
||||
|
||||
// Act
|
||||
val actual = fetcher(params)
|
||||
|
||||
|
|
@ -443,7 +491,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
userTokensSaver.push(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
userTokensSaver.store(userWalletId = params.userWalletId, response = defaultResponse)
|
||||
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = defaultResponse.toLeastTokens())
|
||||
expressServiceFetcher.fetch(userWallet = mockUserWallet, assetIds = defaultResponse.toAssetId())
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
|
|
@ -471,11 +519,11 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
|
|||
),
|
||||
)
|
||||
|
||||
fun UserTokensResponse.toLeastTokens(): List<LeastTokenInfo> {
|
||||
return tokens.map { token ->
|
||||
LeastTokenInfo(
|
||||
contractAddress = token.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE,
|
||||
network = token.networkId,
|
||||
fun UserTokensResponse.toAssetId(): Set<ExpressAsset.ID> {
|
||||
return tokens.mapTo(hashSetOf()) { token ->
|
||||
ExpressAsset.ID(
|
||||
networkId = token.networkId,
|
||||
contractAddress = token.contractAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
|
||||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
|
||||
|
|
@ -26,8 +28,11 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
private val cacheRegistry: CacheRegistry,
|
||||
private val txHistoryItemsStore: TangemPayTxHistoryItemsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
@NetworkMoshi private val moshi: Moshi,
|
||||
) : TangemPayTxHistoryRepository {
|
||||
|
||||
private val txHistoryItemConverter by lazy { TangemPayTxHistoryItemConverter(moshi) }
|
||||
|
||||
override fun getTxHistoryBatchFlow(
|
||||
batchSize: Int,
|
||||
context: TangemPayTxHistoryListBatchingContext,
|
||||
|
|
@ -84,7 +89,7 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
val result = requestPerformer.request { authHeader ->
|
||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||
}.result
|
||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
val items = txHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||
}.onLeft { error(it.toString()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
package com.tangem.data.visa.utils
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.util.Currency
|
||||
|
||||
internal object TangemPayTxHistoryItemConverter :
|
||||
internal class TangemPayTxHistoryItemConverter(moshi: Moshi) :
|
||||
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem?> {
|
||||
|
||||
private val spendAdapter = moshi.adapter(TangemPayTxHistoryResponse.Spend::class.java)
|
||||
private val paymentAdapter = moshi.adapter(TangemPayTxHistoryResponse.Payment::class.java)
|
||||
private val feeAdapter = moshi.adapter(TangemPayTxHistoryResponse.Fee::class.java)
|
||||
|
||||
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem? {
|
||||
return value.spend?.let { convertSpend(id = value.id, spend = it) }
|
||||
?: value.payment?.let { convertPayment(id = value.id, payment = it) }
|
||||
|
|
@ -22,6 +27,7 @@ internal object TangemPayTxHistoryItemConverter :
|
|||
private fun convertSpend(id: String, spend: TangemPayTxHistoryResponse.Spend): TangemPayTxHistoryItem.Spend {
|
||||
return TangemPayTxHistoryItem.Spend(
|
||||
id = id,
|
||||
jsonRepresentation = spendAdapter.toJson(spend),
|
||||
// If postedAt is null, it means transaction wasn't posted and was likely declined. Use authorizedAt
|
||||
date = spend.postedAt ?: spend.authorizedAt,
|
||||
amount = spend.amount,
|
||||
|
|
@ -41,15 +47,18 @@ internal object TangemPayTxHistoryItemConverter :
|
|||
): TangemPayTxHistoryItem.Payment {
|
||||
return TangemPayTxHistoryItem.Payment(
|
||||
id = id,
|
||||
jsonRepresentation = paymentAdapter.toJson(payment),
|
||||
date = payment.postedAt,
|
||||
currency = Currency.getInstance(payment.currency),
|
||||
amount = payment.amount,
|
||||
transactionHash = payment.transactionHash,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertFee(id: String, fee: TangemPayTxHistoryResponse.Fee): TangemPayTxHistoryItem.Fee {
|
||||
return TangemPayTxHistoryItem.Fee(
|
||||
id = id,
|
||||
jsonRepresentation = feeAdapter.toJson(fee),
|
||||
date = fee.postedAt,
|
||||
currency = Currency.getInstance(fee.currency),
|
||||
amount = fee.amount,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue