Updated on 2026-08-14
This commit is contained in:
parent
dab5fb51b6
commit
967d5520a1
37 changed files with 452 additions and 408 deletions
|
|
@ -1,10 +1,7 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.repositories.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -51,6 +48,32 @@ internal object StakingDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchActionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchActionsUseCase {
|
||||
return FetchActionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetActionsUseCase(
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetActionsUseCase {
|
||||
return GetActionsUseCase(
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingTokensUseCase(
|
||||
|
|
@ -123,38 +146,12 @@ internal object StakingDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSavePendingTransactionUseCase(
|
||||
stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SavePendingTransactionUseCase {
|
||||
return SavePendingTransactionUseCase(
|
||||
stakingPendingTransactionRepository = stakingPendingTransactionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInvalidatePendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): InvalidatePendingTransactionsUseCase {
|
||||
return InvalidatePendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository = stakingPendingTransactionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetPendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingPendingTransactionsUseCase {
|
||||
return GetStakingPendingTransactionsUseCase(
|
||||
stakingPendingTransactionRepository = stakingPendingTransactionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ package com.tangem.datasource.api.stakekit
|
|||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.stakekit.models.request.*
|
||||
import com.tangem.datasource.api.stakekit.models.response.EnabledYieldsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.EnterActionResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.ActionDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.GetActionsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingGasEstimateDTO
|
||||
|
|
@ -35,14 +36,23 @@ interface StakeKitApi {
|
|||
@Body body: YieldBalanceRequestBody,
|
||||
): ApiResponse<List<BalanceDTO>>
|
||||
|
||||
@GET("actions")
|
||||
suspend fun getActions(
|
||||
@Query("walletAddress") walletAddress: String,
|
||||
@Query("network") network: String,
|
||||
@Query("status") status: String,
|
||||
@Query("sort") sort: String = "createdAtDesc",
|
||||
@Query("limit") limit: Int = 50,
|
||||
): ApiResponse<GetActionsResponse>
|
||||
|
||||
@POST("actions/enter")
|
||||
suspend fun createEnterAction(@Body body: ActionRequestBody): ApiResponse<EnterActionResponse>
|
||||
suspend fun createEnterAction(@Body body: ActionRequestBody): ApiResponse<ActionDTO>
|
||||
|
||||
@POST("actions/exit")
|
||||
suspend fun createExitAction(@Body body: ActionRequestBody): ApiResponse<EnterActionResponse>
|
||||
suspend fun createExitAction(@Body body: ActionRequestBody): ApiResponse<ActionDTO>
|
||||
|
||||
@POST("actions/pending")
|
||||
suspend fun createPendingAction(@Body body: PendingActionRequestBody): ApiResponse<EnterActionResponse>
|
||||
suspend fun createPendingAction(@Body body: PendingActionRequestBody): ApiResponse<ActionDTO>
|
||||
|
||||
@POST("actions/enter/estimate-gas")
|
||||
suspend fun estimateGasOnEnter(@Body body: ActionRequestBody): ApiResponse<StakingGasEstimateDTO>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import org.joda.time.DateTime
|
|||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class EnterActionResponse(
|
||||
data class ActionDTO(
|
||||
@Json(name = "id")
|
||||
val id: String,
|
||||
@Json(name = "integrationId")
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.api.stakekit.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GetActionsResponse(
|
||||
@Json(name = "data")
|
||||
val data: List<ActionDTO>,
|
||||
@Json(name = "hasNextPage")
|
||||
val hasNextPage: Boolean,
|
||||
@Json(name = "limit")
|
||||
val limit: Int,
|
||||
@Json(name = "page")
|
||||
val page: Int,
|
||||
)
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.token.*
|
||||
import com.tangem.datasource.local.token.DefaultStakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.DefaultStakingYieldsStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -11,11 +12,23 @@ import javax.inject.Singleton
|
|||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object StakingBalanceStoreModule {
|
||||
internal object StakingStoreModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingTokensStore(): StakingYieldsStore {
|
||||
return DefaultStakingYieldsStore()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingBalanceStore(): StakingBalanceStore {
|
||||
return DefaultStakingBalanceStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingActionsStore(): StakingActionsStore {
|
||||
return DefaultStakingActionsStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.token.DefaultStakingYieldsStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
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 StakingTokensStoreModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingTokensStore(): StakingYieldsStore {
|
||||
return DefaultStakingYieldsStore()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
internal class DefaultStakingActionsStore(
|
||||
private val dataStore: StringKeyDataStore<List<StakingAction>>,
|
||||
) : StakingActionsStore {
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
override fun get(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow<List<StakingAction>> {
|
||||
return dataStore.get(composeKey(userWalletId, cryptoCurrencyId))
|
||||
}
|
||||
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
items: List<StakingAction>,
|
||||
) {
|
||||
mutex.withLock {
|
||||
dataStore.store(composeKey(userWalletId, cryptoCurrencyId), items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun composeKey(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): String {
|
||||
return userWalletId.stringValue + cryptoCurrencyId.value
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,13 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
|||
|
||||
internal class DefaultStakingYieldsStore : StakingYieldsStore {
|
||||
|
||||
private var yields = mutableListOf<YieldDTO>()
|
||||
private var yields = listOf<YieldDTO>()
|
||||
|
||||
override fun get(): List<YieldDTO> {
|
||||
return yields
|
||||
}
|
||||
|
||||
override fun store(items: List<YieldDTO>) {
|
||||
yields = items.toMutableList()
|
||||
yields = items
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface StakingActionsStore {
|
||||
|
||||
fun get(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow<List<StakingAction>>
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, items: List<StakingAction>)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import com.tangem.datasource.local.token.StakingActionsStore
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.repositories.StakingActionRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultStakingActionRepository(
|
||||
private val stakingActionsStore: StakingActionsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : StakingActionRepository {
|
||||
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
actions: List<StakingAction>,
|
||||
) {
|
||||
withContext(dispatchers.io) {
|
||||
stakingActionsStore.store(userWalletId, cryptoCurrencyId, actions)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow<List<StakingAction>> {
|
||||
return stakingActionsStore.get(userWalletId, cryptoCurrencyId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
internal class DefaultStakingPendingTransactionRepository : StakingPendingTransactionRepository {
|
||||
|
||||
private val pendingTransactionsMap = ConcurrentHashMap<UserWalletId, CopyOnWriteArrayList<PendingTransaction>>()
|
||||
|
||||
override fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction) {
|
||||
val transactions = pendingTransactionsMap.computeIfAbsent(userWalletId) { CopyOnWriteArrayList() }
|
||||
transactions.add(transaction)
|
||||
}
|
||||
|
||||
override fun removeTransactions(userWalletId: UserWalletId, transactions: Set<PendingTransaction>) {
|
||||
pendingTransactionsMap[userWalletId]?.removeAll(transactions)
|
||||
}
|
||||
|
||||
override fun getTransactionsWithBalanceItems(
|
||||
userWalletId: UserWalletId,
|
||||
): List<Pair<PendingTransaction, BalanceItem>> {
|
||||
return pendingTransactionsMap[userWalletId]?.mapNotNull { pendingTransaction: PendingTransaction ->
|
||||
PendingTransactionItemConverter.convert(pendingTransaction)?.let { balanceItem ->
|
||||
pendingTransaction to balanceItem
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -25,19 +25,19 @@ import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConv
|
|||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.request.*
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.action.StakingActionStatusDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.tron.TronStakeKitTransaction
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.NetworkType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
|
|
@ -102,6 +102,8 @@ internal class DefaultStakingRepository(
|
|||
private val yieldBalanceListConverter = YieldBalanceListConverter(yieldBalanceConverter)
|
||||
|
||||
private val tronStakeKitTransactionAdapter by lazy { moshi.adapter(TronStakeKitTransaction::class.java) }
|
||||
private val networkTypeAdapter by lazy { moshi.adapter(NetworkTypeDTO::class.java) }
|
||||
private val stakingActionStatusAdapter by lazy { moshi.adapter(StakingActionStatusDTO::class.java) }
|
||||
|
||||
override fun getIntegrationKey(cryptoCurrencyId: CryptoCurrency.ID): String = with(cryptoCurrencyId) {
|
||||
rawNetworkId.plus(rawCurrencyId)
|
||||
|
|
@ -140,6 +142,40 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getActions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
networkType: NetworkType,
|
||||
stakingActionStatus: StakingActionStatus,
|
||||
): List<StakingAction> {
|
||||
return withContext(dispatchers.io) {
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
|
||||
|
||||
val networkTypeDto = networkTypeConverter.convertBack(networkType)
|
||||
val networkTypeString = networkTypeDto.extractJsonName()
|
||||
|
||||
val actionStatusDTO = actionStatusConverter.convertBack(stakingActionStatus)
|
||||
val actionStatusString = actionStatusDTO.extractJsonName()
|
||||
|
||||
enterActionResponseConverter.convertListIgnoreErrors(
|
||||
input = stakeKitApi.getActions(
|
||||
walletAddress = address,
|
||||
network = networkTypeString,
|
||||
status = actionStatusString,
|
||||
).getOrThrow().data,
|
||||
onError = { Timber.e("Error converting staking actions list: $it") },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NetworkTypeDTO.extractJsonName(): String {
|
||||
return networkTypeAdapter.toJson(this).replace("\"", "")
|
||||
}
|
||||
|
||||
private fun StakingActionStatusDTO.extractJsonName(): String {
|
||||
return stakingActionStatusAdapter.toJson(this).replace("\"", "")
|
||||
}
|
||||
|
||||
override suspend fun getEntryInfo(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): StakingEntryInfo {
|
||||
return withContext(dispatchers.io) {
|
||||
val yield = getYield(cryptoCurrencyId, symbol)
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
import org.joda.time.DateTime
|
||||
|
||||
internal object PendingTransactionItemConverter : Converter<PendingTransaction, BalanceItem?> {
|
||||
|
||||
override fun convert(value: PendingTransaction): BalanceItem? {
|
||||
return BalanceItem(
|
||||
groupId = value.groupId ?: return null,
|
||||
token = value.token,
|
||||
type = value.type,
|
||||
amount = value.amount,
|
||||
rawCurrencyId = value.rawCurrencyId,
|
||||
validatorAddress = value.validator?.address,
|
||||
date = DateTime.now(),
|
||||
pendingActions = emptyList(),
|
||||
isPending = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,10 @@ package com.tangem.data.staking.converters.action
|
|||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.action.StakingActionStatusDTO
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
|
||||
class ActionStatusConverter : TwoWayConverter<StakingActionStatusDTO, StakingActionStatus> {
|
||||
|
||||
class ActionStatusConverter : Converter<StakingActionStatusDTO, StakingActionStatus> {
|
||||
override fun convert(value: StakingActionStatusDTO): StakingActionStatus {
|
||||
return when (value) {
|
||||
StakingActionStatusDTO.CANCELED -> StakingActionStatus.CANCELED
|
||||
|
|
@ -16,4 +17,16 @@ class ActionStatusConverter : Converter<StakingActionStatusDTO, StakingActionSta
|
|||
else -> StakingActionStatus.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: StakingActionStatus): StakingActionStatusDTO {
|
||||
return when (value) {
|
||||
StakingActionStatus.CANCELED -> StakingActionStatusDTO.CANCELED
|
||||
StakingActionStatus.CREATED -> StakingActionStatusDTO.CREATED
|
||||
StakingActionStatus.WAITING_FOR_NEXT -> StakingActionStatusDTO.WAITING_FOR_NEXT
|
||||
StakingActionStatus.PROCESSING -> StakingActionStatusDTO.PROCESSING
|
||||
StakingActionStatus.FAILED -> StakingActionStatusDTO.FAILED
|
||||
StakingActionStatus.SUCCESS -> StakingActionStatusDTO.SUCCESS
|
||||
else -> StakingActionStatusDTO.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.data.staking.converters.action
|
||||
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionConverter
|
||||
import com.tangem.datasource.api.stakekit.models.response.EnterActionResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.ActionDTO
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
@ -9,9 +9,9 @@ class EnterActionResponseConverter(
|
|||
private val actionStatusConverter: ActionStatusConverter,
|
||||
private val stakingActionTypeConverter: StakingActionTypeConverter,
|
||||
private val transactionConverter: StakingTransactionConverter,
|
||||
) : Converter<EnterActionResponse, StakingAction> {
|
||||
) : Converter<ActionDTO, StakingAction> {
|
||||
|
||||
override fun convert(value: EnterActionResponse): StakingAction {
|
||||
override fun convert(value: ActionDTO): StakingAction {
|
||||
return StakingAction(
|
||||
id = value.id,
|
||||
integrationId = value.integrationId,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.data.staking.di
|
|||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.*
|
||||
import com.tangem.data.staking.DefaultStakingErrorResolver
|
||||
import com.tangem.data.staking.DefaultStakingPendingTransactionRepository
|
||||
import com.tangem.data.staking.DefaultStakingRepository
|
||||
import com.tangem.data.staking.DefaultStakingTransactionHashRepository
|
||||
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
|
||||
|
|
@ -11,12 +11,10 @@ import com.tangem.datasource.api.stakekit.StakeKitApi
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.token.StakingActionsStore
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.repositories.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
|
|
@ -73,8 +71,14 @@ internal object StakingDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingPendingTransactionRepository(): StakingPendingTransactionRepository {
|
||||
return DefaultStakingPendingTransactionRepository()
|
||||
fun provideStakingActionRepository(
|
||||
stakingActionsStore: StakingActionsStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): StakingActionRepository {
|
||||
return DefaultStakingActionRepository(
|
||||
stakingActionsStore = stakingActionsStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.Token
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class PendingTransaction(
|
||||
val groupId: String?,
|
||||
val token: Token,
|
||||
val type: BalanceType,
|
||||
val amount: BigDecimal,
|
||||
val rawCurrencyId: String?,
|
||||
val validator: Yield.Validator?,
|
||||
val balancesId: Int,
|
||||
)
|
||||
|
|
@ -3,5 +3,4 @@ package com.tangem.domain.staking.model
|
|||
data class SubmitHashData(
|
||||
val transactionHash: String,
|
||||
val transactionId: String,
|
||||
val pendingTransaction: PendingTransaction,
|
||||
)
|
||||
|
|
@ -32,14 +32,6 @@ sealed class YieldBalance {
|
|||
.distinctBy { it.validatorAddress }
|
||||
.size
|
||||
}
|
||||
|
||||
fun getBalancesUniqueId(): Int {
|
||||
// need to exclude rewards because their amount may change frequently
|
||||
return balance.items
|
||||
.filter { it.type != BalanceType.REWARDS }
|
||||
.map { it.amount.toString() + it.type.toString() + it.groupId }
|
||||
.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
data object Empty : YieldBalance()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.stakekit.NetworkType
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.staking.repositories.StakingActionRepository
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for getting pending actions list.
|
||||
*/
|
||||
class FetchActionsUseCase(
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val stakingActionRepository: StakingActionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
networkType: NetworkType,
|
||||
stakingActionStatus: StakingActionStatus,
|
||||
): Either<StakingError, Unit> {
|
||||
return Either
|
||||
.catch {
|
||||
val actions = stakingRepository.getActions(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
networkType = networkType,
|
||||
stakingActionStatus = stakingActionStatus,
|
||||
)
|
||||
|
||||
stakingActionRepository.store(userWalletId, cryptoCurrency.id, actions)
|
||||
}
|
||||
.mapLeft { stakingErrorResolver.resolve(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.repositories.StakingActionRepository
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* Use case for getting pending actions list.
|
||||
*/
|
||||
class GetActionsUseCase(
|
||||
private val stakingActionRepository: StakingActionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): EitherFlow<StakingError, List<StakingAction>> {
|
||||
return stakingActionRepository.get(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrencyId,
|
||||
).map<List<StakingAction>, Either<StakingError, List<StakingAction>>> { it.right() }
|
||||
.catch { emit(stakingErrorResolver.resolve(it).left()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for getting saved staking pending transactions.
|
||||
*/
|
||||
class GetStakingPendingTransactionsUseCase(
|
||||
private val stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId): Either<StakingError, List<BalanceItem>> {
|
||||
return Either.catch {
|
||||
stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId).map { it.second }
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,104 +1,85 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
import java.util.UUID
|
||||
|
||||
class InvalidatePendingTransactionsUseCase(
|
||||
private val stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
balanceItems: List<BalanceItem>,
|
||||
balancesId: Int,
|
||||
processingActions: List<StakingAction>,
|
||||
): Either<StakingError, List<BalanceItem>> {
|
||||
return Either.catch {
|
||||
val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions(
|
||||
realData = balanceItems,
|
||||
newBalancesId = balancesId,
|
||||
pendingData = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId),
|
||||
val balancesToDisplay = mergeBalancesAndProcessingActions(
|
||||
realBalances = balanceItems,
|
||||
processingActions = processingActions,
|
||||
)
|
||||
|
||||
stakingPendingTransactionRepository.removeTransactions(userWalletId, transactionsToRemove.toSet())
|
||||
|
||||
balancesToDisplay
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeRealAndPendingTransactions(
|
||||
realData: List<BalanceItem>,
|
||||
newBalancesId: Int,
|
||||
pendingData: List<Pair<PendingTransaction, BalanceItem>>,
|
||||
): Pair<List<BalanceItem>, List<PendingTransaction>> {
|
||||
val balances = realData.groupBy { BalanceIdentity(it.groupId, it.type, it.amount, it.date) }
|
||||
.mapValues { it.value.toMutableList() }
|
||||
.toMutableMap()
|
||||
private fun mergeBalancesAndProcessingActions(
|
||||
realBalances: List<BalanceItem>,
|
||||
processingActions: List<StakingAction>,
|
||||
): List<BalanceItem> {
|
||||
val balances = realBalances.toMutableList()
|
||||
|
||||
val transactionsToRemove = mutableListOf<PendingTransaction>()
|
||||
|
||||
pendingData.forEach { (pendingTransaction, balanceItem) ->
|
||||
val key = BalanceIdentity(balanceItem.groupId, balanceItem.type, balanceItem.amount, balanceItem.date)
|
||||
val oldBalancesId = pendingTransaction.balancesId
|
||||
|
||||
when {
|
||||
newBalancesId != oldBalancesId -> {
|
||||
transactionsToRemove.add(pendingTransaction)
|
||||
processingActions.forEach { action ->
|
||||
when (action.type) {
|
||||
StakingActionType.STAKE, StakingActionType.VOTE, StakingActionType.VOTE_LOCKED -> {
|
||||
processEnterAction(balances, action)
|
||||
}
|
||||
balances.containsKey(key) -> {
|
||||
val removed = balances[key]?.removeIf { !it.isPending } ?: false
|
||||
if (removed) {
|
||||
balances[key]?.add(balanceItem)
|
||||
}
|
||||
StakingActionType.WITHDRAW -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.UNSTAKED)
|
||||
}
|
||||
StakingActionType.UNLOCK_LOCKED -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.LOCKED)
|
||||
}
|
||||
StakingActionType.UNSTAKE -> {
|
||||
modifyBalancesByStatus(balances, action, BalanceType.STAKED)
|
||||
}
|
||||
else -> {
|
||||
val groupId = UUID.randomUUID().toString()
|
||||
val now = DateTime.now()
|
||||
|
||||
balances[BalanceIdentity(groupId, BalanceType.STAKED, pendingTransaction.amount, now)] =
|
||||
mutableListOf(
|
||||
BalanceItem(
|
||||
groupId = groupId,
|
||||
type = pendingTransaction.type,
|
||||
amount = pendingTransaction.amount,
|
||||
rawCurrencyId = pendingTransaction.rawCurrencyId,
|
||||
validatorAddress = pendingTransaction.validator?.address,
|
||||
date = null,
|
||||
pendingActions = emptyList(),
|
||||
token = pendingTransaction.token,
|
||||
isPending = true,
|
||||
),
|
||||
)
|
||||
|
||||
balances.entries.find {
|
||||
it.key.type == BalanceType.STAKED &&
|
||||
it.key.amount == pendingTransaction.amount &&
|
||||
!it.value.any { it.isPending }
|
||||
}
|
||||
?.key
|
||||
?.let { balances.remove(it) }
|
||||
// intentionally do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return balances.values.flatten() to transactionsToRemove
|
||||
return balances
|
||||
}
|
||||
|
||||
private data class BalanceIdentity(
|
||||
val groupId: String,
|
||||
val type: BalanceType,
|
||||
val amount: BigDecimal,
|
||||
val date: DateTime?,
|
||||
)
|
||||
private fun processEnterAction(balances: MutableList<BalanceItem>, action: StakingAction) {
|
||||
balances.add(
|
||||
BalanceItem(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
token = balances[0].token,
|
||||
type = BalanceType.STAKED,
|
||||
amount = action.amount,
|
||||
rawCurrencyId = null,
|
||||
validatorAddress = action.validatorAddress ?: action.validatorAddresses?.get(0) ?: "",
|
||||
date = null,
|
||||
pendingActions = emptyList(),
|
||||
isPending = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun modifyBalancesByStatus(balances: MutableList<BalanceItem>, action: StakingAction, type: BalanceType) {
|
||||
val index = balances.indexOfFirst {
|
||||
!it.isPending && it.amount == action.amount && it.type == type
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
balances[index] = balances[index].copy(isPending = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for saving hash that failed to submit during staking confirmation
|
||||
*/
|
||||
class SavePendingTransactionUseCase(
|
||||
private val stakingPendingTransactionRepository: StakingPendingTransactionRepository,
|
||||
private val stakingErrorResolver: StakingErrorResolver,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, transaction: PendingTransaction): Either<StakingError, Unit> {
|
||||
return Either.catch {
|
||||
stakingPendingTransactionRepository.saveTransaction(
|
||||
userWalletId = userWalletId,
|
||||
transaction = transaction,
|
||||
)
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.domain.staking.repositories
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface StakingActionRepository {
|
||||
|
||||
suspend fun store(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, actions: List<StakingAction>)
|
||||
|
||||
fun get(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow<List<StakingAction>>
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package com.tangem.domain.staking.repositories
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface StakingPendingTransactionRepository {
|
||||
|
||||
fun getTransactionsWithBalanceItems(userWalletId: UserWalletId): List<Pair<PendingTransaction, BalanceItem>>
|
||||
|
||||
fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction)
|
||||
|
||||
fun removeTransactions(userWalletId: UserWalletId, transactions: Set<PendingTransaction>)
|
||||
}
|
||||
|
|
@ -6,10 +6,12 @@ import com.tangem.blockchain.common.transaction.Fee
|
|||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.NetworkType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
|
|
@ -33,6 +35,13 @@ interface StakingRepository {
|
|||
|
||||
fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
|
||||
|
||||
suspend fun getActions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
networkType: NetworkType,
|
||||
stakingActionStatus: StakingActionStatus,
|
||||
): List<StakingAction>
|
||||
|
||||
suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.tokens.error
|
|||
|
||||
sealed class CurrencyStatusError {
|
||||
|
||||
object UnableToCreateCurrency : CurrencyStatusError()
|
||||
data object UnableToCreateCurrency : CurrencyStatusError()
|
||||
|
||||
data class DataError(val cause: Throwable) : CurrencyStatusError()
|
||||
}
|
||||
|
|
@ -117,6 +117,15 @@ class MockStakingRepository : StakingRepository {
|
|||
cryptoCurrency: CryptoCurrency,
|
||||
): StakingAvailability = StakingAvailability.Unavailable
|
||||
|
||||
override suspend fun getActions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
networkType: NetworkType,
|
||||
stakingActionStatus: StakingActionStatus,
|
||||
): List<StakingAction> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
|||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
|
|
@ -120,7 +119,6 @@ internal sealed class StakingStates {
|
|||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
val possiblePendingTransaction: PendingTransaction?,
|
||||
) : ConfirmationState()
|
||||
|
||||
data class Empty(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.helpers
|
||||
|
||||
import com.tangem.domain.staking.FetchStakingYieldBalanceUseCase
|
||||
import com.tangem.domain.staking.FetchActionsUseCase
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -20,16 +23,21 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
private val stakingYieldBalanceUseCase: FetchStakingYieldBalanceUseCase,
|
||||
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val fetchActionsUseCase: FetchActionsUseCase,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val yield: Yield,
|
||||
) {
|
||||
fun scheduleUpdates() {
|
||||
coroutineScope.launch {
|
||||
listOf(
|
||||
// we should update network to find pending tx after 1 sec
|
||||
async {
|
||||
fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrencyStatus.currency.network))
|
||||
fetchPendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networks = setOf(cryptoCurrencyStatus.currency.network),
|
||||
)
|
||||
},
|
||||
// we should update tx history and network for new balances
|
||||
async {
|
||||
|
|
@ -41,6 +49,9 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
async {
|
||||
updateNetworkStatuses()
|
||||
},
|
||||
async {
|
||||
updateProcessingActions()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +65,9 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
async {
|
||||
updateNetworkStatuses(delay = 0)
|
||||
},
|
||||
async {
|
||||
updateProcessingActions()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
|
@ -91,9 +105,22 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun updateProcessingActions() {
|
||||
fetchActionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
networkType = yield.token.network,
|
||||
stakingActionStatus = StakingActionStatus.PROCESSING,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(cryptoCurrencyStatus: CryptoCurrencyStatus, userWallet: UserWallet): StakingBalanceUpdater
|
||||
fun create(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWallet: UserWallet,
|
||||
yield: Yield,
|
||||
): StakingBalanceUpdater
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.blockchain.common.transaction.Fee
|
|||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.SubmitHashData
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
|
|
@ -36,7 +35,6 @@ import kotlinx.coroutines.awaitAll
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class StakingTransactionSender @AssistedInject constructor(
|
||||
|
|
@ -48,7 +46,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val savePendingTransactionUseCase: SavePendingTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
|
|
@ -57,7 +54,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
private val balanceUpdater: StakingBalanceUpdater
|
||||
get() = stakingBalanceUpdater.create(cryptoCurrencyStatus, userWallet)
|
||||
get() = stakingBalanceUpdater.create(cryptoCurrencyStatus, userWallet, yield)
|
||||
|
||||
suspend fun constructAndSendTransactions(
|
||||
onConstructSuccess: (List<StakingTransaction>) -> Unit,
|
||||
|
|
@ -95,16 +92,11 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
possiblePendingTransaction = composePendingTransaction(confirmationState),
|
||||
onSendSuccess = onSendSuccess,
|
||||
onSendError = onSendError,
|
||||
)
|
||||
}
|
||||
|
||||
private fun composePendingTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
return confirmationState.possiblePendingTransaction ?: composeStakeTransaction()
|
||||
}
|
||||
|
||||
private suspend fun getStakingTransactions(
|
||||
state: StakingUiState,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
|
|
@ -214,7 +206,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
private suspend fun sendStakingTransaction(
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
possiblePendingTransaction: PendingTransaction,
|
||||
onSendSuccess: (txUrl: String) -> Unit,
|
||||
onSendError: (SendTransactionError?) -> Unit,
|
||||
) {
|
||||
|
|
@ -230,7 +221,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
submitHash(
|
||||
transactions = fullTransactionsData.map { it.stakeKitTransaction },
|
||||
transactionHashes = transactionHashes,
|
||||
pendingTransaction = possiblePendingTransaction,
|
||||
)
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
|
|
@ -243,11 +233,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun submitHash(
|
||||
transactions: List<StakingTransaction>,
|
||||
transactionHashes: List<String>,
|
||||
pendingTransaction: PendingTransaction,
|
||||
) {
|
||||
private suspend fun submitHash(transactions: List<StakingTransaction>, transactionHashes: List<String>) {
|
||||
transactions
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transaction, transactionHash) ->
|
||||
|
|
@ -255,7 +241,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
SubmitHashData(
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
pendingTransaction = pendingTransaction,
|
||||
),
|
||||
)
|
||||
.onLeft { error ->
|
||||
|
|
@ -271,9 +256,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
if (transaction.type != StakingTransactionType.FREEZE_ENERGY) {
|
||||
savePendingTransactionUseCase.invoke(userWallet.walletId, pendingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -292,23 +274,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun composeStakeTransaction(): PendingTransaction {
|
||||
val state = stateController.value
|
||||
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId)
|
||||
|
||||
return PendingTransaction(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
token = token,
|
||||
type = BalanceType.STAKED,
|
||||
amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO,
|
||||
rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId,
|
||||
validator = (state.validatorState as? StakingStates.ValidatorState.Data)?.chosenValidator,
|
||||
balancesId = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
)
|
||||
}
|
||||
|
||||
private data class FullTransactionData(
|
||||
val stakeKitTransaction: StakingTransaction,
|
||||
val tangemTransaction: TransactionData.Compiled,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,5 @@ internal object ConfirmationStatePreviewData {
|
|||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
pendingActions = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -63,7 +63,6 @@ internal class SetConfirmationStateInitTransformer(
|
|||
reduceAmountBy = null,
|
||||
pendingAction = pendingAction,
|
||||
pendingActions = pendingActions.takeIf { isComposePendingActions },
|
||||
possiblePendingTransaction = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetPossiblePendingTransactionTransformer(
|
||||
private val yield: Yield,
|
||||
private val balanceState: BalanceState,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val possibleConfirmationState =
|
||||
prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
|
||||
val balancesId = yieldBalance?.getBalancesUniqueId() ?: 0
|
||||
val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId)
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(
|
||||
possiblePendingTransaction = PendingTransaction(
|
||||
groupId = balanceState.groupId,
|
||||
token = token,
|
||||
type = balanceState.type,
|
||||
amount = balanceState.cryptoDecimal,
|
||||
rawCurrencyId = balanceState.rawCurrencyId,
|
||||
validator = balanceState.validator,
|
||||
balancesId = balancesId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -25,14 +25,13 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
|||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.staking.InvalidatePendingTransactionsUseCase
|
||||
import com.tangem.domain.staking.IsAnyTokenStakedUseCase
|
||||
import com.tangem.domain.staking.IsApproveNeededUseCase
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.tokens.*
|
||||
|
|
@ -74,7 +73,6 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import javax.inject.Inject
|
||||
|
|
@ -108,6 +106,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val stakingBalanceUpdater: StakingBalanceUpdater.Factory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getActionsUseCase: GetActionsUseCase,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
|
||||
|
|
@ -131,6 +130,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
?: error("This screen can't be opened without `Yield`")
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var processingActions: List<StakingAction> = emptyList()
|
||||
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
|
||||
private var innerRouter: InnerStakingRouter by Delegates.notNull()
|
||||
|
|
@ -141,9 +141,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
get() {
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
return invalidatePendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
|
||||
balancesId = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
processingActions = processingActions,
|
||||
).getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +152,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingBalanceUpdater.create(
|
||||
cryptoCurrencyStatus,
|
||||
userWallet,
|
||||
yield,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -215,11 +215,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
override fun onNextClick(balanceState: BalanceState?) {
|
||||
stakingStateRouter.onNextClick()
|
||||
if (balanceState != null && (isInitState() || isAssentState())) {
|
||||
stateController.update(
|
||||
SetPossiblePendingTransactionTransformer(yield, balanceState, cryptoCurrencyStatus),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFee() {
|
||||
|
|
@ -281,7 +276,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
transactionsInProgress.addAll(constructedTransactions)
|
||||
},
|
||||
onConstructError = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakekitError(
|
||||
token = value.cryptoCurrencySymbol,
|
||||
|
|
@ -297,7 +291,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(SetConfirmationStateCompletedTransformer(txUrl))
|
||||
},
|
||||
onSendError = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
|
|
@ -527,7 +520,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
userWalletId = userWalletId,
|
||||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
|
|
@ -549,7 +541,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
network = tokenCryptoCurrency.network,
|
||||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencySymbol))
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
|
|
@ -760,7 +751,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
userWallet = wallet
|
||||
},
|
||||
ifLeft = {
|
||||
Timber.e(it.toString())
|
||||
stakingEventFactory.createGenericErrorAlert(it.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -790,10 +780,10 @@ internal class StakingViewModel @Inject constructor(
|
|||
setupApprovalNeeded()
|
||||
setupIsAnyTokenStaked()
|
||||
checkIfSubtractAvailable()
|
||||
subscribeOnActionsUpdates()
|
||||
subscribeOnStepChanges()
|
||||
},
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
stakingEventFactory.createGenericErrorAlert(error.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -831,18 +821,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
.onEach {
|
||||
when {
|
||||
isInitState() -> {
|
||||
stateController.updateAll(
|
||||
SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = isAnyTokenStaked,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
SetConfirmationStateEmptyTransformer,
|
||||
)
|
||||
updateInitialData()
|
||||
onRefreshSwipe(isRefreshing = false)
|
||||
}
|
||||
isAssentState() -> {
|
||||
|
|
@ -862,6 +841,40 @@ internal class StakingViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnActionsUpdates() {
|
||||
getActionsUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrencyId,
|
||||
)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { result ->
|
||||
result.getOrNull()?.let { actions ->
|
||||
processingActions = actions
|
||||
if (isInitState()) {
|
||||
updateInitialData()
|
||||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun updateInitialData() {
|
||||
stateController.updateAll(
|
||||
SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = isAnyTokenStaked,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
SetConfirmationStateEmptyTransformer,
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareForConfirmation(
|
||||
pendingActions: ImmutableList<PendingAction> = persistentListOf(),
|
||||
pendingAction: PendingAction? = pendingActions.firstOrNull(),
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.GetStakingPendingTransactionsUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
||||
|
|
@ -121,7 +120,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val associateAssetUseCase: AssociateAssetUseCase,
|
||||
private val getStakingPendingTransactionsUseCase: GetStakingPendingTransactionsUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
|
|
@ -151,8 +149,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val warningsJobHolder = JobHolder()
|
||||
private val swapTxJobHolder = JobHolder()
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
private val stakingPendingBalances: List<BalanceItem>
|
||||
get() = getStakingPendingTransactionsUseCase(userWalletId).getOrElse { emptyList() }
|
||||
private val stakingPendingBalances: List<BalanceItem> = emptyList() // TODO staking
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var stakingEntryInfo: StakingEntryInfo? = null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue