Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-09 13:09:47 +05:00
parent 3f5f3ef68b
commit d1a09370c7
39 changed files with 317 additions and 301 deletions

View file

@ -12,6 +12,9 @@ internal class RuntimeUserWalletsStore(
private val walletsStateHolder: WalletsStateHolder,
) : UserWalletsStore {
override val selectedUserWalletOrNull: UserWallet?
get() = walletsStateHolder.userWalletsListManager?.selectedUserWalletSync
override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? {
return walletsStateHolder.userWalletsListManager
?.userWallets

View file

@ -1,86 +0,0 @@
package com.tangem.tap.proxy
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
import com.tangem.blockchain.common.txhistory.TransactionHistoryState
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.lib.crypto.TxHistoryManager
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryItem
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryState
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionStatus
class TxHistoryManagerImpl(
private val appStateHolder: AppStateHolder,
) : TxHistoryManager {
override suspend fun checkTxHistoryState(networkId: String, derivationPath: String?): ProxyTransactionHistoryState {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain, derivationPath)
val state = walletManager.getTransactionHistoryState(address = walletManager.wallet.address)
return state.mapToProxy()
}
override suspend fun getTxHistoryItems(
networkId: String,
derivationPath: String?,
page: Int,
pageSize: Int,
): List<ProxyTransactionHistoryItem> {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain, derivationPath)
val itemsResult = walletManager.getTransactionsHistory(
address = walletManager.wallet.address,
page = page,
pageSize = pageSize,
)
return when (itemsResult) {
is Result.Success -> itemsResult.data.map { historyItem -> historyItem.mapToProxy() }
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
}
}
private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList())
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
return requireNotNull(walletManager) { "no wallet manager found" }
}
private fun TransactionHistoryState.mapToProxy(): ProxyTransactionHistoryState {
return when (this) {
TransactionHistoryState.Success.Empty -> ProxyTransactionHistoryState.Success.Empty
is TransactionHistoryState.Failed.FetchError -> ProxyTransactionHistoryState.Failed.FetchError(exception)
TransactionHistoryState.NotImplemented -> ProxyTransactionHistoryState.NotImplemented
is TransactionHistoryState.Success.HasTransactions ->
ProxyTransactionHistoryState.Success.HasTransactions(txCount)
}
}
private fun TransactionHistoryItem.mapToProxy() = ProxyTransactionHistoryItem(
txHash = txHash,
timestamp = timestamp,
direction = when (val direction = direction) {
is TransactionHistoryItem.TransactionDirection.Incoming ->
ProxyTransactionHistoryItem.TransactionDirection.Incoming(direction.from)
is TransactionHistoryItem.TransactionDirection.Outgoing ->
ProxyTransactionHistoryItem.TransactionDirection.Outgoing(direction.to)
},
status = when (status) {
TransactionStatus.Confirmed -> ProxyTransactionStatus.Confirmed
TransactionStatus.Unconfirmed -> ProxyTransactionStatus.Unconfirmed
},
type = when (type) {
TransactionHistoryItem.TransactionType.Transfer -> ProxyTransactionHistoryItem.TransactionType.Transfer
},
amount = ProxyAmount(
currencySymbol = amount.currencySymbol,
value = requireNotNull(amount.value) { "Amount value must not be null" },
decimals = amount.decimals,
),
)
}

View file

@ -8,7 +8,6 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.TxHistoryManager
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.tap.proxy.*
import dagger.Module
@ -59,12 +58,6 @@ class ProxyModule {
)
}
@Provides
@Singleton
fun provideTxHistoryManager(appStateHolder: AppStateHolder): TxHistoryManager {
return TxHistoryManagerImpl(appStateHolder = appStateHolder)
}
// regions FeatureConsumers
@Provides
@Singleton

View file

@ -5,5 +5,7 @@ import com.tangem.domain.wallets.models.UserWalletId
interface UserWalletsStore {
val selectedUserWalletOrNull: UserWallet?
suspend fun getSyncOrNull(key: UserWalletId): UserWallet?
}

View file

@ -10,7 +10,13 @@ android {
}
dependencies {
implementation(projects.core.utils)
implementation(projects.core.datasource)
implementation(projects.domain.legacy)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets.models)
implementation(deps.kotlin.coroutines)
implementation(deps.androidx.paging.runtime)

View file

@ -1,7 +1,9 @@
package com.tangem.data.txhistory.di
import com.tangem.data.txhistory.repository.MockTxHistoryRepository
import com.tangem.data.txhistory.repository.DefaultTxHistoryRepository
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -14,5 +16,11 @@ internal object TxHistoryDataModule {
@Provides
@Singleton
fun provideTxHistoryRepository(): TxHistoryRepository = MockTxHistoryRepository()
fun provideTxHistoryRepository(
walletManagersFacade: WalletManagersFacade,
userWalletsStore: UserWalletsStore,
): TxHistoryRepository = DefaultTxHistoryRepository(
walletManagersFacade = walletManagersFacade,
userWalletsStore = userWalletsStore,
)
}

View file

@ -1,70 +0,0 @@
package com.tangem.data.txhistory.mock
import com.tangem.domain.txhistory.model.TxHistoryItem
import java.math.BigDecimal
internal object MockTxHistoryItems {
private val txHistoryItem1 = TxHistoryItem(
txHash = "noster",
timestamp = System.currentTimeMillis(),
direction = TxHistoryItem.TransactionDirection.Incoming("address"),
status = TxHistoryItem.TxStatus.Confirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
private val txHistoryItem2 = TxHistoryItem(
txHash = "noster",
timestamp = 1689844346000,
direction = TxHistoryItem.TransactionDirection.Incoming("address2"),
status = TxHistoryItem.TxStatus.Unconfirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
private val txHistoryItem3 = TxHistoryItem(
txHash = "noster",
timestamp = 1689757946000,
direction = TxHistoryItem.TransactionDirection.Outgoing("address3"),
status = TxHistoryItem.TxStatus.Confirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
private val txHistoryItem4 = TxHistoryItem(
txHash = "noster",
timestamp = 1689671546000,
direction = TxHistoryItem.TransactionDirection.Incoming("address4"),
status = TxHistoryItem.TxStatus.Confirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
private val txHistoryItem5 = TxHistoryItem(
txHash = "noster",
timestamp = 1689585146000,
direction = TxHistoryItem.TransactionDirection.Outgoing("address5"),
status = TxHistoryItem.TxStatus.Unconfirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
private val txHistoryItem6 = TxHistoryItem(
txHash = "noster",
timestamp = 1689585146000,
direction = TxHistoryItem.TransactionDirection.Incoming("address6"),
status = TxHistoryItem.TxStatus.Confirmed,
type = TxHistoryItem.TransactionType.Transfer,
amount = BigDecimal("1000000000.5"),
)
val txHistoryItems = listOf(
txHistoryItem1,
txHistoryItem2,
txHistoryItem3,
txHistoryItem4,
txHistoryItem5,
txHistoryItem6,
)
}

View file

@ -0,0 +1,66 @@
package com.tangem.data.txhistory.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.coroutines.flow.Flow
class DefaultTxHistoryRepository(
private val walletManagersFacade: WalletManagersFacade,
private val userWalletsStore: UserWalletsStore,
) : TxHistoryRepository {
override suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int {
val userWallet = getUserWallet()
val state = walletManagersFacade.getTxHistoryState(
userWalletId = userWallet.walletId,
networkId = networkId,
rawDerivationPath = derivationPath,
)
return when (state) {
is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception)
TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented
TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories
is TxHistoryState.Success.HasTransactions -> state.txCount
}
}
override fun getTxHistoryItems(
networkId: Network.ID,
derivationPath: String?,
pageSize: Int,
): Flow<PagingData<TxHistoryItem>> {
val userWallet = getUserWallet()
return Pager(
config = PagingConfig(
pageSize = pageSize,
),
pagingSourceFactory = {
TxHistoryPagingSource(
loadPage = { page: Int, pageSize: Int ->
walletManagersFacade.getTxHistoryItems(
userWalletId = userWallet.walletId,
networkId = networkId,
rawDerivationPath = derivationPath,
page = page,
pageSize = pageSize,
)
},
)
},
).flow
}
private fun getUserWallet(): UserWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull) {
"Selected wallet must not be null"
}
}

View file

@ -1,25 +0,0 @@
package com.tangem.data.txhistory.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import kotlinx.coroutines.flow.Flow
internal class MockTxHistoryRepository : TxHistoryRepository {
override suspend fun getTxHistoryItemsCount(networkId: String, derivationPath: String): Int {
return 0
}
override fun getTxHistoryItems(networkId: String, pageSize: Int): Flow<PagingData<TxHistoryItem>> {
return Pager(
config = PagingConfig(
pageSize = pageSize,
),
pagingSourceFactory = { TxHistoryPagingSource() },
).flow
}
}

View file

@ -2,12 +2,14 @@ package com.tangem.data.txhistory.repository.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.tangem.data.txhistory.mock.MockTxHistoryItems
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.txhistory.models.PaginationWrapper
import com.tangem.domain.txhistory.models.TxHistoryItem
private const val INITIAL_PAGE = 1
internal class TxHistoryPagingSource : PagingSource<Int, TxHistoryItem>() {
internal class TxHistoryPagingSource(
private val loadPage: suspend (page: Int, pageSize: Int) -> PaginationWrapper<TxHistoryItem>,
) : PagingSource<Int, TxHistoryItem>() {
override fun getRefreshKey(state: PagingState<Int, TxHistoryItem>): Int? {
return state.anchorPosition?.let { anchorPosition ->
@ -19,20 +21,12 @@ internal class TxHistoryPagingSource : PagingSource<Int, TxHistoryItem>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TxHistoryItem> {
val currentPage = params.key ?: INITIAL_PAGE
return try {
// TODO: [REDACTED_JIRA]
// val result = txHistoryManager.getTxHistoryItems(
// networkId = networkId,
// derivationPath = derivationPath,
// page = currentPage,
// pageSize = params.loadSize,
// )
val result = MockTxHistoryItems.txHistoryItems
val result = loadPage(currentPage, params.loadSize)
LoadResult.Page(
data = result,
data = result.items,
prevKey = if (currentPage > INITIAL_PAGE) currentPage.minus(1) else null,
// TODO: handle end of reached [REDACTED_JIRA]
nextKey = null,
nextKey = if (result.page < result.totalPages) currentPage.plus(1) else null,
)
} catch (e: Exception) {
LoadResult.Error(e)

View file

@ -13,6 +13,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets.models)
/** Tangem libraries */

View file

@ -3,6 +3,7 @@ package com.tangem.domain.walletmanager
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.extensions.Result
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.local.userwallet.UserWalletsStore
@ -12,10 +13,11 @@ import com.tangem.domain.common.util.hasDerivation
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.PaginationWrapper
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryState
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
import com.tangem.domain.walletmanager.utils.SdkTokenConverter
import com.tangem.domain.walletmanager.utils.UpdateWalletManagerResultFactory
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
import com.tangem.domain.walletmanager.utils.*
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import timber.log.Timber
@ -32,24 +34,22 @@ class DefaultWalletManagersFacade(
private val resultFactory by lazy { UpdateWalletManagerResultFactory() }
private val walletManagerFactory by lazy { WalletManagerFactory(configManager) }
private val sdkTokenConverter by lazy { SdkTokenConverter() }
private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() }
private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter() }
override suspend fun update(
userWalletId: UserWalletId,
networkId: Network.ID,
extraTokens: Set<CryptoCurrency.Token>,
): UpdateWalletManagerResult {
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"Unable to find a user wallet with provided ID: $userWalletId"
}
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(networkId.value)
return getAndUpdateWalletManager(userWallet, blockchain, extraTokens)
}
override suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String {
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"Unable to find a user wallet with provided ID: $userWalletId"
}
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(networkId.value)
@ -63,6 +63,57 @@ class DefaultWalletManagersFacade(
.orEmpty()
}
override suspend fun getTxHistoryState(
userWalletId: UserWalletId,
networkId: Network.ID,
rawDerivationPath: String?,
): TxHistoryState {
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(networkId.value)
val derivationPath = rawDerivationPath?.let(::DerivationPath)
val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) {
"Unable to get a wallet manager for blockchain: $blockchain"
}
return walletManager
.getTransactionHistoryState(walletManager.wallet.address)
.let(txHistoryStateConverter::convert)
}
override suspend fun getTxHistoryItems(
userWalletId: UserWalletId,
networkId: Network.ID,
rawDerivationPath: String?,
page: Int,
pageSize: Int,
): PaginationWrapper<TxHistoryItem> {
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(networkId.value)
val derivationPath = rawDerivationPath?.let(::DerivationPath)
val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) {
"Unable to get a wallet manager for blockchain: $blockchain"
}
val itemsResult = walletManager.getTransactionsHistory(
address = walletManager.wallet.address,
page = page,
pageSize = pageSize,
)
return when (itemsResult) {
is Result.Success -> PaginationWrapper(
page = itemsResult.data.page,
totalPages = itemsResult.data.totalPages,
itemsOnPage = itemsResult.data.itemsOnPage,
items = txHistoryItemConverter.convertList(itemsResult.data.items),
)
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
}
}
private suspend fun getUserWallet(userWalletId: UserWalletId) =
requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"Unable to find a user wallet with provided ID: $userWalletId"
}
private suspend fun getAndUpdateWalletManager(
userWallet: UserWallet,
blockchain: Blockchain,

View file

@ -2,6 +2,9 @@ package com.tangem.domain.walletmanager
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.PaginationWrapper
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryState
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
import com.tangem.domain.wallets.models.UserWalletId
@ -26,4 +29,35 @@ interface WalletManagersFacade {
): UpdateWalletManagerResult
suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String
/**
* Returns transactions count
*
* @param userWalletId The ID of the user's wallet.
* @param networkId The network ID.
* @param rawDerivationPath Derivation path in raw form.
*/
suspend fun getTxHistoryState(
userWalletId: UserWalletId,
networkId: Network.ID,
rawDerivationPath: String?,
): TxHistoryState
/**
* Returns transaction history items wrapped to pagination
*
* @param userWalletId The ID of the user's wallet.
* @param networkId The network ID.
* @param rawDerivationPath Derivation path in raw form.
* @param page Pagination page.
* @param pageSize Pagination size.
*/
suspend fun getTxHistoryItems(
userWalletId: UserWalletId,
networkId: Network.ID,
rawDerivationPath: String?,
page: Int,
pageSize: Int,
): PaginationWrapper<TxHistoryItem>
}

View file

@ -0,0 +1,29 @@
package com.tangem.domain.walletmanager.utils
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.utils.converter.Converter
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem as SdkTransactionHistoryItem
internal class SdkTransactionHistoryItemConverter : Converter<SdkTransactionHistoryItem, TxHistoryItem> {
override fun convert(value: SdkTransactionHistoryItem): TxHistoryItem = TxHistoryItem(
txHash = value.txHash,
timestampInMillis = value.timestamp,
direction = when (val direction = value.direction) {
is SdkTransactionHistoryItem.TransactionDirection.Incoming ->
TxHistoryItem.TransactionDirection.Incoming(direction.from)
is SdkTransactionHistoryItem.TransactionDirection.Outgoing ->
TxHistoryItem.TransactionDirection.Outgoing(direction.to)
},
status = when (value.status) {
TransactionStatus.Confirmed -> TxHistoryItem.TxStatus.Confirmed
TransactionStatus.Unconfirmed -> TxHistoryItem.TxStatus.Unconfirmed
},
type = when (value.type) {
TransactionHistoryItem.TransactionType.Transfer -> TxHistoryItem.TransactionType.Transfer
},
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
)
}

View file

@ -0,0 +1,16 @@
package com.tangem.domain.walletmanager.utils
import com.tangem.blockchain.common.txhistory.TransactionHistoryState
import com.tangem.domain.txhistory.models.TxHistoryState
import com.tangem.utils.converter.Converter
import com.tangem.blockchain.common.txhistory.TransactionHistoryState as SdkTransactionHistoryState
internal class SdkTransactionHistoryStateConverter : Converter<SdkTransactionHistoryState, TxHistoryState> {
override fun convert(value: TransactionHistoryState): TxHistoryState = when (value) {
is TransactionHistoryState.Success.Empty -> TxHistoryState.Success.Empty
is TransactionHistoryState.Success.HasTransactions -> TxHistoryState.Success.HasTransactions(value.txCount)
is TransactionHistoryState.Failed.FetchError -> TxHistoryState.Failed.FetchError(value.exception)
is TransactionHistoryState.NotImplemented -> TxHistoryState.NotImplemented
}
}

View file

@ -14,4 +14,6 @@ dependencies {
implementation(deps.androidx.paging.runtime)
implementation(projects.core.utils)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models)
}

1
domain/txhistory/models/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,4 @@
plugins {
alias(deps.plugins.kotlin.jvm)
id("configuration")
}

View file

@ -0,0 +1,8 @@
package com.tangem.domain.txhistory.models
data class PaginationWrapper<T>(
val page: Int,
val totalPages: Int,
val itemsOnPage: Int,
val items: List<T>,
)

View file

@ -1,10 +1,10 @@
package com.tangem.domain.txhistory.model
package com.tangem.domain.txhistory.models
import java.math.BigDecimal
data class TxHistoryItem(
val txHash: String,
val timestamp: Long,
val timestampInMillis: Long,
val direction: TransactionDirection,
val status: TxStatus,
val type: TransactionType,

View file

@ -1,4 +1,4 @@
package com.tangem.domain.txhistory.error
package com.tangem.domain.txhistory.models
sealed class TxHistoryListError : Throwable() {
data class DataError(override val cause: Throwable) : TxHistoryListError()

View file

@ -0,0 +1,15 @@
package com.tangem.domain.txhistory.models
sealed class TxHistoryState {
sealed class Success : TxHistoryState() {
object Empty : Success()
data class HasTransactions(val txCount: Int) : Success()
}
sealed class Failed : TxHistoryState() {
data class FetchError(val exception: Exception) : Failed()
}
object NotImplemented : TxHistoryState()
}

View file

@ -1,4 +1,4 @@
package com.tangem.domain.txhistory.error
package com.tangem.domain.txhistory.models
sealed class TxHistoryStateError : Throwable() {

View file

@ -1,16 +1,21 @@
package com.tangem.domain.txhistory.repository
import androidx.paging.PagingData
import com.tangem.domain.txhistory.error.TxHistoryListError
import com.tangem.domain.txhistory.error.TxHistoryStateError
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.models.TxHistoryItem
import kotlinx.coroutines.flow.Flow
interface TxHistoryRepository {
@Throws(TxHistoryStateError::class)
suspend fun getTxHistoryItemsCount(networkId: String, derivationPath: String): Int
suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int
@Throws(TxHistoryListError::class)
fun getTxHistoryItems(networkId: String, pageSize: Int): Flow<PagingData<TxHistoryItem>>
fun getTxHistoryItems(
networkId: Network.ID,
derivationPath: String?,
pageSize: Int,
): Flow<PagingData<TxHistoryItem>>
}

View file

@ -3,12 +3,13 @@ package com.tangem.domain.txhistory.usecase
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.txhistory.error.TxHistoryStateError
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.repository.TxHistoryRepository
class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) {
suspend operator fun invoke(networkId: String, derivationPath: String): Either<TxHistoryStateError, Int> {
suspend operator fun invoke(networkId: Network.ID, derivationPath: String?): Either<TxHistoryStateError, Int> {
return either {
catch(
block = { repository.getTxHistoryItemsCount(networkId, derivationPath) },

View file

@ -3,8 +3,9 @@ package com.tangem.domain.txhistory.usecase
import androidx.paging.PagingData
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.domain.txhistory.error.TxHistoryListError
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
@ -14,12 +15,13 @@ private const val DEFAULT_PAGE_SIZE = 20
class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) {
operator fun invoke(
networkId: String,
networkId: Network.ID,
derivationPath: String?,
pageSize: Int = DEFAULT_PAGE_SIZE,
): Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>> {
return either {
repository
.getTxHistoryItems(networkId = networkId, pageSize = pageSize)
.getTxHistoryItems(networkId = networkId, derivationPath = derivationPath, pageSize = pageSize)
.catch { raise(TxHistoryListError.DataError(it)) }
}
}

View file

@ -27,6 +27,7 @@ dependencies {
implementation(deps.compose.coil)
implementation(deps.kotlin.immutable.collections)
implementation(deps.arrow.core)
/** DI */
implementation(deps.hilt.android)
@ -37,6 +38,9 @@ dependencies {
implementation(projects.core.ui)
implementation(projects.core.navigation)
implementation(projects.domain.txhistory)
implementation(projects.domain.txhistory.models)
/** Feature Apis */
implementation(projects.features.tokendetails.api)
}

View file

@ -53,6 +53,7 @@ dependencies {
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)

View file

@ -7,9 +7,9 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.txhistory.error.TxHistoryListError
import com.tangem.domain.txhistory.error.TxHistoryStateError
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState

View file

@ -6,8 +6,8 @@ import com.tangem.common.Provider
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.txhistory.error.TxHistoryListError
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton

View file

@ -7,7 +7,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.TransactionState
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.txhistory.error.TxHistoryStateError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton

View file

@ -4,7 +4,7 @@ import android.text.format.DateUtils
import androidx.paging.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.components.transactions.TransactionState
import com.tangem.domain.txhistory.model.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
@ -180,7 +180,7 @@ internal class WalletTxHistoryItemFlowConverter(
*
* @see [convert]
*/
private fun TxHistoryItem.getRawTimestamp() = this.timestamp.toString()
private fun TxHistoryItem.getRawTimestamp() = this.timestampInMillis.toString()
private fun TxHistoryItemState?.getTimestamp(): Long? {
return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) {

View file

@ -5,12 +5,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.*
import androidx.paging.cachedIn
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.common.Provider
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.domain.card.*
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
@ -143,23 +143,30 @@ internal class WalletViewModel @Inject constructor(
private fun updateByTxHistory(index: Int) {
viewModelScope.launch(dispatchers.io) {
val blockchain = getWallet(index).scanResponse.cardTypesResolver.getBlockchain()
val wallet = getWallet(index)
val blockchain = wallet.scanResponse.cardTypesResolver.getBlockchain()
val derivationPath = blockchain.derivationPath(style = wallet.scanResponse.card.derivationStyle)?.rawPath
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
networkId = blockchain.id,
derivationPath = requireNotNull(blockchain.derivationPath(style = DerivationStyle.LEGACY)).rawPath,
networkId = Network.ID(blockchain.id),
derivationPath = derivationPath,
)
uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither)
txHistoryItemsCountEither.onRight { updateTxHistory(networkId = blockchain.id) }
txHistoryItemsCountEither.onRight {
updateTxHistory(
networkId = Network.ID(blockchain.id),
derivationPath = derivationPath,
)
}
updateNotifications(index)
}
}
private fun updateTxHistory(networkId: String) {
private fun updateTxHistory(networkId: Network.ID, derivationPath: String?) {
uiState = stateFactory.getLoadedTxHistoryState(
txHistoryEither = txHistoryItemsUseCase(networkId = networkId).map { it.cachedIn(viewModelScope) },
txHistoryEither = txHistoryItemsUseCase(networkId, derivationPath).map { it.cachedIn(viewModelScope) },
)
}

View file

@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "develop-297"
tangemBlockchainSdk = "develop-306"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-280"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds

View file

@ -1,18 +0,0 @@
package com.tangem.lib.crypto
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryItem
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryState
interface TxHistoryManager {
@Throws(IllegalStateException::class)
suspend fun checkTxHistoryState(networkId: String, derivationPath: String?): ProxyTransactionHistoryState
@Throws(IllegalStateException::class)
suspend fun getTxHistoryItems(
networkId: String,
derivationPath: String?,
page: Int,
pageSize: Int,
): List<ProxyTransactionHistoryItem>
}

View file

@ -1,21 +0,0 @@
package com.tangem.lib.crypto.models.txhistory
import com.tangem.lib.crypto.models.ProxyAmount
data class ProxyTransactionHistoryItem(
val txHash: String,
val timestamp: Long,
val direction: TransactionDirection,
val status: ProxyTransactionStatus,
val type: TransactionType,
val amount: ProxyAmount,
) {
sealed interface TransactionDirection {
data class Incoming(val from: String) : TransactionDirection
data class Outgoing(val to: String) : TransactionDirection
}
sealed interface TransactionType {
object Transfer : TransactionType
}
}

View file

@ -1,15 +0,0 @@
package com.tangem.lib.crypto.models.txhistory
sealed class ProxyTransactionHistoryState {
sealed class Success : ProxyTransactionHistoryState() {
object Empty : Success()
data class HasTransactions(val txCount: Int) : Success()
}
sealed class Failed : ProxyTransactionHistoryState() {
data class FetchError(val exception: Exception) : Failed()
}
object NotImplemented : ProxyTransactionHistoryState()
}

View file

@ -1,3 +0,0 @@
package com.tangem.lib.crypto.models.txhistory
enum class ProxyTransactionStatus { Confirmed, Unconfirmed }

View file

@ -107,6 +107,7 @@ include(":domain:tokens:models")
include(":domain:wallets")
include(":domain:wallets:models")
include(":domain:txhistory")
include(":domain:txhistory:models")
// endregion Domain modules
// region Data modules