Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-09 10:10:35 +03:00
parent a74c3787e7
commit e49a9befc7
12 changed files with 137 additions and 97 deletions

View file

@ -583,8 +583,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
private fun sendStakingUnsubmittedHashes() {
lifecycleScope.launch {
sendUnsubmittedHashesUseCase.invoke()
.onRight { Timber.d("Submitting hashes succeeded") }
.onLeft { Timber.e(it.toString()) }
.onRight { Timber.d("Submitting hashes succeeded") }
}
}
}

View file

@ -3,6 +3,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.StakingRepository
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -112,11 +113,11 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideSubmitHashUseCase(
stakingRepository: StakingRepository,
stakingTransactionHashRepository: StakingTransactionHashRepository,
stakingErrorResolver: StakingErrorResolver,
): SubmitHashUseCase {
return SubmitHashUseCase(
stakingRepository = stakingRepository,
stakingTransactionHashRepository = stakingTransactionHashRepository,
stakingErrorResolver = stakingErrorResolver,
)
}
@ -124,11 +125,11 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideSaveUnsubmittedHashUseCase(
stakingRepository: StakingRepository,
stakingTransactionHashRepository: StakingTransactionHashRepository,
stakingErrorResolver: StakingErrorResolver,
): SaveUnsubmittedHashUseCase {
return SaveUnsubmittedHashUseCase(
stakingRepository = stakingRepository,
stakingTransactionHashRepository = stakingTransactionHashRepository,
stakingErrorResolver = stakingErrorResolver,
)
}
@ -136,11 +137,11 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideSendUnsubmittedHashesUseCase(
stakingRepository: StakingRepository,
stakingTransactionHashRepository: StakingTransactionHashRepository,
stakingErrorResolver: StakingErrorResolver,
): SendUnsubmittedHashesUseCase {
return SendUnsubmittedHashesUseCase(
stakingRepository = stakingRepository,
stakingTransactionHashRepository = stakingTransactionHashRepository,
stakingErrorResolver = stakingErrorResolver,
)
}

View file

@ -44,6 +44,7 @@ dependencies {
implementation(deps.kotlin.immutable.collections)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.timber)
implementation(projects.libs.blockchainSdk)
implementation(projects.libs.crypto)

View file

@ -26,9 +26,6 @@ import com.tangem.datasource.api.stakekit.StakeKitApi
import com.tangem.datasource.api.stakekit.models.request.*
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.api.stakekit.models.response.model.transaction.tron.TronStakeKitTransaction
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectListSync
import com.tangem.datasource.local.token.StakingBalanceStore
import com.tangem.datasource.local.token.StakingYieldsStore
import com.tangem.domain.core.lce.LceFlow
@ -36,7 +33,10 @@ import com.tangem.domain.core.lce.lceFlow
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.UnsubmittedTransactionMetadata
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
@ -54,7 +54,6 @@ import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
import com.tangem.lib.crypto.BlockchainUtils.isSolana
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -62,7 +61,6 @@ import kotlinx.coroutines.withContext
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
internal class DefaultStakingRepository(
private val stakeKitApi: StakeKitApi,
private val appPreferencesStore: AppPreferencesStore,
private val stakingYieldsStore: StakingYieldsStore,
private val stakingBalanceStore: StakingBalanceStore,
private val cacheRegistry: CacheRegistry,
@ -464,55 +462,6 @@ internal class DefaultStakingRepository(
}
}
override suspend fun submitHash(transactionId: String, transactionHash: String) {
withContext(dispatchers.io) {
stakeKitApi.submitTransactionHash(
transactionId = transactionId,
body = SubmitTransactionHashRequestBody(
hash = transactionHash,
),
)
}
}
override suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata) {
withContext(dispatchers.io) {
appPreferencesStore.editData { preferences ->
val savedTransactions = preferences.getObjectListOrDefault<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
default = emptyList(),
)
preferences.setObjectList(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
value = savedTransactions + unsubmittedTransactionMetadata,
)
}
}
}
override suspend fun sendUnsubmittedHashes() {
withContext(NonCancellable) {
val savedTransactions = appPreferencesStore.getObjectListSync<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
)
savedTransactions.forEach {
stakeKitApi.submitTransactionHash(
transactionId = it.transactionId,
body = SubmitTransactionHashRequestBody(hash = it.transactionHash),
)
}
appPreferencesStore.editData { mutablePreferences ->
mutablePreferences.setObjectList<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
value = emptyList(),
)
}
}
}
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean {
return withContext(dispatchers.io) {
stakingBalanceStore.getSyncOrNull(userWalletId)

View file

@ -0,0 +1,83 @@
package com.tangem.data.staking
import com.tangem.datasource.api.stakekit.StakeKitApi
import com.tangem.datasource.api.stakekit.models.request.*
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectListSync
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
internal class DefaultStakingTransactionHashRepository(
private val stakeKitApi: StakeKitApi,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : StakingTransactionHashRepository {
override suspend fun submitHash(transactionId: String, transactionHash: String) {
withContext(dispatchers.io) {
stakeKitApi.submitTransactionHash(
transactionId = transactionId,
body = SubmitTransactionHashRequestBody(
hash = transactionHash,
),
)
}
}
override suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata) {
withContext(dispatchers.io) {
appPreferencesStore.editData { preferences ->
val savedTransactions = preferences.getObjectListOrDefault<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
default = emptyList(),
)
preferences.setObjectList(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
value = savedTransactions + unsubmittedTransactionMetadata,
)
}
}
}
override suspend fun sendUnsubmittedHashes() {
withContext(dispatchers.io) {
val savedTransactions = appPreferencesStore.getObjectListSync<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
)
savedTransactions.forEach { transaction ->
try {
stakeKitApi.submitTransactionHash(
transactionId = transaction.transactionId,
body = SubmitTransactionHashRequestBody(hash = transaction.transactionHash),
)
appPreferencesStore.editData { mutablePreferences ->
val updatedTransactions =
mutablePreferences.getObjectListOrDefault<UnsubmittedTransactionMetadata>(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
default = emptyList(),
).filterNot { it.transactionId == transaction.transactionId }
mutablePreferences.setObjectList(
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
value = updatedTransactions,
)
}
} catch (e: Exception) {
val logMessage = buildString {
append("Error while submitting transaction with\n")
append("StakeKit id = ${transaction.transactionId} and\n")
append("transaction hash = ${transaction.transactionHash}")
}
Timber.e(logMessage)
}
}
}
}
}

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Moshi
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.staking.DefaultStakingErrorResolver
import com.tangem.data.staking.DefaultStakingRepository
import com.tangem.data.staking.DefaultStakingTransactionHashRepository
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
import com.tangem.datasource.api.stakekit.StakeKitApi
import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse
@ -13,6 +14,7 @@ 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.StakingRepository
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
@ -31,23 +33,21 @@ internal object StakingDataModule {
@Singleton
fun provideStakingRepository(
stakeKitApi: StakeKitApi,
appPreferencesStore: AppPreferencesStore,
stakingTokenStore: StakingYieldsStore,
stakingYieldsStore: StakingYieldsStore,
stakingBalanceStore: StakingBalanceStore,
cacheRegistry: CacheRegistry,
dispatchers: CoroutineDispatcherProvider,
stakingFeatureToggle: StakingFeatureToggles,
cacheRegistry: CacheRegistry,
walletManagersFacade: WalletManagersFacade,
getUserWalletUseCase: GetUserWalletUseCase,
@NetworkMoshi moshi: Moshi,
): StakingRepository {
return DefaultStakingRepository(
stakeKitApi = stakeKitApi,
appPreferencesStore = appPreferencesStore,
stakingYieldsStore = stakingTokenStore,
stakingYieldsStore = stakingYieldsStore,
stakingBalanceStore = stakingBalanceStore,
dispatchers = dispatchers,
cacheRegistry = cacheRegistry,
dispatchers = dispatchers,
stakingFeatureToggle = stakingFeatureToggle,
walletManagersFacade = walletManagersFacade,
getUserWalletUseCase = getUserWalletUseCase,
@ -55,6 +55,20 @@ internal object StakingDataModule {
)
}
@Provides
@Singleton
fun StakingTransactionHashRepository(
stakeKitApi: StakeKitApi,
appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
): StakingTransactionHashRepository {
return DefaultStakingTransactionHashRepository(
stakeKitApi = stakeKitApi,
appPreferencesStore = appPreferencesStore,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
internal fun provideStakingErrorResolver(@NetworkMoshi moshi: Moshi): StakingErrorResolver {

View file

@ -4,19 +4,19 @@ import arrow.core.Either
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
/**
* Use case for saving hash that failed to submit during staking confirmation
*/
class SaveUnsubmittedHashUseCase(
private val stakingRepository: StakingRepository,
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(transactionId: String, transactionHash: String): Either<StakingError, Unit> {
return Either.catch {
stakingRepository.storeUnsubmittedHash(
stakingTransactionHashRepository.storeUnsubmittedHash(
unsubmittedTransactionMetadata = UnsubmittedTransactionMetadata(
transactionId = transactionId,
transactionHash = transactionHash,

View file

@ -3,19 +3,19 @@ package com.tangem.domain.staking
import arrow.core.Either
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
/**
* Use case for commiting hashes that failed to submit during staking confirmation
*/
class SendUnsubmittedHashesUseCase(
private val stakingRepository: StakingRepository,
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(): Either<StakingError, Unit> {
return Either
.catch { stakingRepository.sendUnsubmittedHashes() }
.catch { stakingTransactionHashRepository.sendUnsubmittedHashes() }
.mapLeft { stakingErrorResolver.resolve(it) }
}
}

View file

@ -3,20 +3,20 @@ package com.tangem.domain.staking
import arrow.core.Either
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
/**
* Use case for submitting transaction hash to stakekit
*/
class SubmitHashUseCase(
private val stakingRepository: StakingRepository,
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend fun submitHash(transactionId: String, transactionHash: String): Either<StakingError, Unit> {
return Either
.catch {
stakingRepository.submitHash(
stakingTransactionHashRepository.submitHash(
transactionId = transactionId,
transactionHash = transactionHash,
)

View file

@ -6,7 +6,6 @@ import com.tangem.domain.core.lce.LceFlow
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.UnsubmittedTransactionMetadata
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
@ -75,12 +74,6 @@ interface StakingRepository {
transactionId: String,
): Pair<StakingTransaction, TransactionData.Compiled>
suspend fun submitHash(transactionId: String, transactionHash: String)
suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata)
suspend fun sendUnsubmittedHashes()
/** Returns staking approval */
fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval

View file

@ -0,0 +1,12 @@
package com.tangem.domain.staking.repositories
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
interface StakingTransactionHashRepository {
suspend fun submitHash(transactionId: String, transactionHash: String)
suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata)
suspend fun sendUnsubmittedHashes()
}

View file

@ -8,7 +8,6 @@ import com.tangem.domain.core.lce.lceFlow
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.UnsubmittedTransactionMetadata
import com.tangem.domain.staking.model.stakekit.*
import com.tangem.domain.staking.model.stakekit.action.StakingAction
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
@ -239,18 +238,6 @@ class MockStakingRepository : StakingRepository {
status = TransactionStatus.Unconfirmed,
)
override suspend fun submitHash(transactionId: String, transactionHash: String) {
/* no-op */
}
override suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata) {
/* no-op */
}
override suspend fun sendUnsubmittedHashes() {
/* no-op */
}
override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval = StakingApproval.Empty
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean = false