Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-24 13:48:52 +02:00
parent aa6c1c95f2
commit df32ea7ab6
32 changed files with 704 additions and 637 deletions

View file

@ -6,14 +6,14 @@ import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
import com.tangem.domain.models.wallet.UserWalletId
/**
* Use case for staking gas estimation.
*/
class EstimateGasUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -23,7 +23,7 @@ class EstimateGasUseCase(
params: ActionParams,
): Either<StakingError, StakingGasEstimate> {
return Either.catch {
stakingRepository.estimateGas(userWalletId, network, params)
stakeKitRepository.estimateGas(userWalletId, network, params)
}.mapLeft {
stakingErrorResolver.resolve(it)
}

View file

@ -5,17 +5,17 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.staking.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.StakeKitActionRepository
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
import com.tangem.domain.models.wallet.UserWalletId
/**
* Use case for getting pending actions list.
*/
class FetchActionsUseCase(
private val stakingRepository: StakingRepository,
private val stakingActionRepository: StakingActionRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakeKitActionRepository: StakeKitActionRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -27,14 +27,14 @@ class FetchActionsUseCase(
): Either<StakingError, Unit> {
return Either
.catch {
val actions = stakingRepository.getActions(
val actions = stakeKitRepository.getActions(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
networkType = networkType,
stakingActionStatus = stakingActionStatus,
)
stakingActionRepository.store(userWalletId, cryptoCurrency.id, actions)
stakeKitActionRepository.store(userWalletId, cryptoCurrency.id, actions)
}
.mapLeft { stakingErrorResolver.resolve(it) }
}

View file

@ -6,7 +6,7 @@ import arrow.core.raise.either
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
* Fetches both StakeKit yields and P2P vaults
*/
class FetchStakingOptionsUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val p2pRepository: P2PEthPoolRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -24,7 +24,7 @@ class FetchStakingOptionsUseCase(
catch(
block = {
coroutineScope {
launch { stakingRepository.fetchYields() }
launch { stakeKitRepository.fetchYields() }
launch { p2pRepository.fetchVaults() }
}
},

View file

@ -5,19 +5,19 @@ import arrow.core.raise.catch
import arrow.core.raise.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.StakeKitRepository
/**
* Use case for getting enabled tokens
*/
class FetchStakingTokensUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(): Either<StakingError, Unit> {
return either {
catch(
block = { stakingRepository.fetchYields() },
block = { stakeKitRepository.fetchYields() },
catch = { stakingErrorResolver.resolve(it) },
)
}

View file

@ -7,7 +7,7 @@ import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.models.currency.CryptoCurrency
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.StakeKitActionRepository
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.catch
@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.map
* Use case for getting pending actions list.
*/
class GetActionsUseCase(
private val stakingActionRepository: StakingActionRepository,
private val stakeKitActionRepository: StakeKitActionRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -25,7 +25,7 @@ class GetActionsUseCase(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
): EitherFlow<StakingError, List<StakingAction>> {
return stakingActionRepository.get(
return stakeKitActionRepository.get(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrencyId,
).map<List<StakingAction>, Either<StakingError, List<StakingAction>>> { it.right() }

View file

@ -7,10 +7,10 @@ import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
class GetConstructedStakingTransactionUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -20,7 +20,7 @@ class GetConstructedStakingTransactionUseCase(
amount: Amount,
transactionId: String,
): Either<StakingError, Pair<StakingTransaction, TransactionData.Compiled>> = Either.catch {
stakingRepository.constructTransaction(networkId, fee, amount, transactionId)
stakeKitRepository.constructTransaction(networkId, fee, amount, transactionId)
}.mapLeft {
stakingErrorResolver.resolve(it)
}

View file

@ -8,8 +8,8 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.StakingAvailability
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.models.wallet.UserWalletId
import com.tangem.domain.staking.repositories.StakingRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map

View file

@ -6,13 +6,13 @@ import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.model.StakingOption
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.StakeKitRepository
/**
* Use case for getting entry info about staking on token screen.
*/
class GetStakingEntryInfoUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -25,7 +25,7 @@ class GetStakingEntryInfoUseCase(
.catch {
when (stakingOption) {
is StakingOption.StakeKit -> {
stakingRepository.getEntryInfo(
stakeKitRepository.getEntryInfo(
cryptoCurrencyId = cryptoCurrencyId,
symbol = symbol,
)

View file

@ -6,7 +6,7 @@ import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.delay
@ -14,7 +14,7 @@ import kotlinx.coroutines.delay
* Use case for creating enter action
*/
class GetStakingTransactionsUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
@ -24,7 +24,7 @@ class GetStakingTransactionsUseCase(
params: ActionParams,
): Either<StakingError, List<StakingTransaction>> {
return Either.catch {
val createAction = stakingRepository.createAction(userWalletId, network, params)
val createAction = stakeKitRepository.createAction(userWalletId, network, params)
// workaround, sometimes transaction is not created immediately after actions/enter
delay(PATCH_TRANSACTION_REQUEST_DELAY)

View file

@ -5,25 +5,25 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.repositories.StakingErrorResolver
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
/**
* Use case for getting staking yield for staking scenario start.
*/
class GetYieldUseCase(
private val stakingRepository: StakingRepository,
private val stakeKitRepository: StakeKitRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Either<StakingError, Yield> {
return Either
.catch { stakingRepository.getYield(cryptoCurrencyId, symbol) }
.catch { stakeKitRepository.getYield(cryptoCurrencyId, symbol) }
.mapLeft { stakingErrorResolver.resolve(it) }
}
suspend operator fun invoke(yieldId: String): Either<StakingError, Yield> {
return Either
.catch { stakingRepository.getYield(yieldId) }
.catch { stakeKitRepository.getYield(yieldId) }
.mapLeft { stakingErrorResolver.resolve(it) }
}
}

View file

@ -3,8 +3,8 @@ 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.models.wallet.UserWalletId
import com.tangem.domain.staking.repositories.StakingRepository
class IsAnyTokenStakedUseCase(
private val stakingRepository: StakingRepository,

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.StakingTransactionHashRepository
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
/**
* Use case for saving hash that failed to submit during staking confirmation
*/
class SaveUnsubmittedHashUseCase(
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
private val stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(transactionId: String, transactionHash: String): Either<StakingError, Unit> {
return Either.catch {
stakingTransactionHashRepository.storeUnsubmittedHash(
stakeKitTransactionHashRepository.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.StakingTransactionHashRepository
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
/**
* Use case for commiting hashes that failed to submit during staking confirmation
*/
class SendUnsubmittedHashesUseCase(
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
private val stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
private val stakingErrorResolver: StakingErrorResolver,
) {
suspend operator fun invoke(): Either<StakingError, Unit> {
return Either
.catch { stakingTransactionHashRepository.sendUnsubmittedHashes() }
.catch { stakeKitTransactionHashRepository.sendUnsubmittedHashes() }
.mapLeft { stakingErrorResolver.resolve(it) }
}
}

View file

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

View file

@ -1,8 +1,10 @@
package com.tangem.domain.staking.repositories
import arrow.core.Either
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.ethpool.*
import com.tangem.domain.staking.model.stakekit.StakingError
import kotlinx.coroutines.flow.Flow
interface P2PEthPoolRepository {
@ -113,4 +115,20 @@ interface P2PEthPoolRepository {
vaultAddress: String,
period: Int? = null,
): Either<StakingError, List<P2PEthPoolReward>>
/**
* Check P2P staking availability by finding public vault
*
* @return Flow of StakingAvailability - Available with StakingOption.P2P if public vault found,
* TemporaryUnavailable if not found or vaults empty
*/
fun getStakingAvailability(): Flow<StakingAvailability>
/**
* Check P2P staking availability synchronously
*
* @return StakingAvailability - Available with StakingOption.P2P if public vault found,
* TemporaryUnavailable if not found or vaults empty
*/
suspend fun getStakingAvailabilitySync(): StakingAvailability
}

View file

@ -5,7 +5,7 @@ import com.tangem.domain.staking.model.stakekit.action.StakingAction
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
interface StakingActionRepository {
interface StakeKitActionRepository {
suspend fun store(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, actions: List<StakingAction>)

View file

@ -0,0 +1,54 @@
package com.tangem.domain.staking.repositories
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.models.staking.NetworkType
import com.tangem.domain.staking.model.stakekit.Yield
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
import kotlinx.coroutines.flow.Flow
@Suppress("TooManyFunctions")
interface StakeKitRepository {
suspend fun fetchYields()
fun getEnabledYields(): Flow<List<Yield>>
fun getStakingAvailability(rawCurrencyId: CryptoCurrency.RawID, symbol: String): Flow<StakingAvailability>
suspend fun getStakingAvailabilitySync(rawCurrencyId: CryptoCurrency.RawID, symbol: String): StakingAvailability
suspend fun getEntryInfo(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): StakingEntryInfo
suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield
suspend fun getYield(yieldId: String): Yield
suspend fun getActions(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
networkType: NetworkType,
stakingActionStatus: StakingActionStatus,
): List<StakingAction>
suspend fun createAction(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingAction
suspend fun estimateGas(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingGasEstimate
suspend fun constructTransaction(
networkId: String,
fee: Fee,
amount: Amount,
transactionId: String,
): Pair<StakingTransaction, TransactionData.Compiled>
}

View file

@ -2,7 +2,7 @@ package com.tangem.domain.staking.repositories
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
interface StakingTransactionHashRepository {
interface StakeKitTransactionHashRepository {
suspend fun submitHash(transactionId: String, transactionHash: String)

View file

@ -1,36 +1,12 @@
package com.tangem.domain.staking.repositories
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.models.staking.NetworkType
import com.tangem.domain.staking.model.stakekit.Yield
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
import kotlinx.coroutines.flow.Flow
// TODO p2p make 3 repos: stakekit, p2p, common staking
@Suppress("TooManyFunctions")
interface StakingRepository {
suspend fun fetchYields()
fun getEnabledYields(): Flow<List<Yield>>
suspend fun getEntryInfo(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): StakingEntryInfo
suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield
suspend fun getYield(yieldId: String): Yield
fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow<StakingAvailability>
suspend fun getStakingAvailabilitySync(
@ -38,23 +14,5 @@ interface StakingRepository {
cryptoCurrency: CryptoCurrency,
): StakingAvailability
suspend fun getActions(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
networkType: NetworkType,
stakingActionStatus: StakingActionStatus,
): List<StakingAction>
suspend fun createAction(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingAction
suspend fun estimateGas(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingGasEstimate
suspend fun constructTransaction(
networkId: String,
fee: Fee,
amount: Amount,
transactionId: String,
): Pair<StakingTransaction, TransactionData.Compiled>
suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean
}

View file

@ -1,7 +1,7 @@
package com.tangem.domain.staking.usecase
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.repositories.StakeKitRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -12,10 +12,10 @@ import kotlinx.coroutines.flow.map
* - key: currency staking key (network.backendId + "_" + symbol)
* - value: validators
*/
class StakingApyFlowUseCase(private val stakingRepository: StakingRepository) {
class StakingApyFlowUseCase(private val stakeKitRepository: StakeKitRepository) {
operator fun invoke(): Flow<Map<String, List<Yield.Validator>>> {
return stakingRepository.getEnabledYields()
return stakeKitRepository.getEnabledYields()
.map { yields ->
yields.associate { yield ->
val key = "${yield.token.coinGeckoId}_${yield.token.symbol}"