Updated on 2026-08-14
This commit is contained in:
parent
aa6c1c95f2
commit
df32ea7ab6
32 changed files with 704 additions and 637 deletions
|
|
@ -2,10 +2,11 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||
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.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
|
@ -35,11 +36,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingEntryInfoUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingEntryInfoUseCase {
|
||||
return GetStakingEntryInfoUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -47,11 +48,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetYieldUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetYieldUseCase {
|
||||
return GetYieldUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -59,13 +60,13 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchActionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakeKitActionRepository: StakeKitActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchActionsUseCase {
|
||||
return FetchActionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakeKitActionRepository = stakeKitActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -73,11 +74,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetActionsUseCase(
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakeKitActionRepository: StakeKitActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetActionsUseCase {
|
||||
return GetActionsUseCase(
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakeKitActionRepository = stakeKitActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -85,11 +86,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingTokensUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchStakingTokensUseCase {
|
||||
return FetchStakingTokensUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -97,12 +98,12 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchStakingOptionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
p2pRepository: P2PEthPoolRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchStakingOptionsUseCase {
|
||||
return FetchStakingOptionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
p2pRepository = p2pRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
|
|
@ -123,11 +124,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingTransactionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingTransactionsUseCase {
|
||||
return GetStakingTransactionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -135,11 +136,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGasEstimateUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): EstimateGasUseCase {
|
||||
return EstimateGasUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -147,11 +148,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSubmitHashUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SubmitHashUseCase {
|
||||
return SubmitHashUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -159,11 +160,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveUnsubmittedHashUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SaveUnsubmittedHashUseCase {
|
||||
return SaveUnsubmittedHashUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -181,11 +182,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSendUnsubmittedHashesUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SendUnsubmittedHashesUseCase {
|
||||
return SendUnsubmittedHashesUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -193,11 +194,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetConstructedStakingTransactionUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetConstructedStakingTransactionUseCase {
|
||||
return GetConstructedStakingTransactionUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -236,7 +237,7 @@ internal object StakingDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingApyFlowUseCase(stakingRepository: StakingRepository): StakingApyFlowUseCase {
|
||||
return StakingApyFlowUseCase(stakingRepository)
|
||||
fun provideStakingApyFlowUseCase(stakeKitRepository: StakeKitRepository): StakingApyFlowUseCase {
|
||||
return StakingApyFlowUseCase(stakeKitRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>MultilineLambdaItParameter:DefaultMultiYieldBalanceFetcher.kt$DefaultMultiYieldBalanceFetcher${ Timber.e(it, "Unable to fetch yield balances $params") yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds) }</ID>
|
||||
<ID>MultilineLambdaItParameter:DefaultMultiYieldBalanceFetcher.kt$DefaultMultiYieldBalanceFetcher${ Timber.e(it, "Unable to fetch yield balances $userWalletId") yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds) throw it }</ID>
|
||||
<ID>MultilineLambdaItParameter:DefaultMultiYieldBalanceFetcher.kt$DefaultMultiYieldBalanceFetcher${ request.integrationId == it.integrationId && request.addresses.address == it.addresses.address }</ID>
|
||||
<ID>MultilineLambdaItParameter:DefaultMultiYieldBalanceFetcher.kt$DefaultMultiYieldBalanceFetcher${ stakingId.integrationId == it.integrationId && stakingId.address == it.addresses.address }</ID>
|
||||
<ID>MultilineLambdaItParameter:DefaultStakingRepository.kt$DefaultStakingRepository${ YieldConverter.convertListIgnoreErrors( input = it, onError = { Timber.e("Error converting one of the items in enabled yields: $it") }, ) }</ID>
|
||||
<ID>NamedArguments:DefaultP2PEthPoolRepository.kt$DefaultP2PEthPoolRepository$getRewards(network.value, delegatorAddress, vaultAddress, period)</ID>
|
||||
<ID>NoNameShadowing:DefaultStakingRepository.kt$DefaultStakingRepository${ Timber.e("Error converting one of the items in enabled yields: $it") }</ID>
|
||||
<ID>NullableToStringCall:DefaultStakingRepository.kt$DefaultStakingRepository$${(response as? ApiResponse.Error)?.cause}</ID>
|
||||
<ID>UseEmptyCounterpart:YieldBalanceRequestBodyFactory.kt$YieldBalanceRequestBodyFactory$listOf()</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -12,10 +12,15 @@ import com.tangem.datasource.api.ethpool.models.request.P2PEthPoolDepositRequest
|
|||
import com.tangem.datasource.api.ethpool.models.request.P2PEthPoolUnstakeRequest
|
||||
import com.tangem.datasource.api.ethpool.models.request.P2PEthPoolWithdrawRequest
|
||||
import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.staking.model.ethpool.*
|
||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -192,7 +197,12 @@ internal class DefaultP2PEthPoolRepository(
|
|||
period: Int?,
|
||||
): Either<StakingError, List<P2PEthPoolReward>> = either {
|
||||
withContext(dispatchers.io) {
|
||||
val response = p2pApi.getRewards(network.value, delegatorAddress, vaultAddress, period)
|
||||
val response = p2pApi.getRewards(
|
||||
network = network.value,
|
||||
delegatorAddress = delegatorAddress,
|
||||
vaultAddress = vaultAddress,
|
||||
period = period,
|
||||
)
|
||||
when (response) {
|
||||
is ApiResponse.Success -> {
|
||||
val data = response.data
|
||||
|
|
@ -206,4 +216,49 @@ internal class DefaultP2PEthPoolRepository(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStakingAvailability(): Flow<StakingAvailability> {
|
||||
return getVaultsFlow()
|
||||
.distinctUntilChanged()
|
||||
.map { vaults ->
|
||||
if (vaults.isEmpty()) {
|
||||
return@map StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val vault = findPublicVault(vaults = vaults)
|
||||
|
||||
if (vault != null) {
|
||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
||||
} else {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getStakingAvailabilitySync(): StakingAvailability {
|
||||
val vaults = getVaultsSync()
|
||||
if (vaults.isEmpty()) {
|
||||
return StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val vault = findPublicVault(vaults = vaults)
|
||||
|
||||
return if (vault != null) {
|
||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
||||
} else {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getVaultsSync(): List<P2PEthPoolVault> {
|
||||
return p2pEthPoolVaultsStore.getSync()
|
||||
}
|
||||
|
||||
private fun getVaultsFlow(): Flow<List<P2PEthPoolVault>> {
|
||||
return p2pEthPoolVaultsStore.get()
|
||||
}
|
||||
|
||||
private fun findPublicVault(vaults: List<P2PEthPoolVault>): P2PEthPoolVault? {
|
||||
return vaults.firstOrNull { vault -> !vault.isPrivate }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,15 +4,15 @@ import com.tangem.datasource.local.token.StakingActionsStore
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
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.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultStakingActionRepository(
|
||||
internal class DefaultStakeKitActionRepository(
|
||||
private val stakingActionsStore: StakingActionsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : StakingActionRepository {
|
||||
) : StakeKitActionRepository {
|
||||
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -0,0 +1,421 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import android.util.Base64
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toCompressedPublicKey
|
||||
import com.tangem.data.staking.converters.YieldConverter
|
||||
import com.tangem.data.staking.converters.action.ActionStatusConverter
|
||||
import com.tangem.data.staking.converters.action.EnterActionResponseConverter
|
||||
import com.tangem.data.staking.converters.transaction.GasEstimateConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionStatusConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConverter
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
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.EnabledYieldsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO
|
||||
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.StakingYieldsStore
|
||||
import com.tangem.datasource.local.token.converter.StakingNetworkTypeConverter
|
||||
import com.tangem.datasource.local.token.converter.YieldTokenConverter
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.staking.NetworkType
|
||||
import com.tangem.domain.models.staking.action.StakingActionType
|
||||
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.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
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.StakingActionCommonType
|
||||
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 com.tangem.domain.staking.repositories.StakeKitRepository
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
internal class DefaultStakeKitRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
moshi: Moshi,
|
||||
) : StakeKitRepository {
|
||||
|
||||
private val transactionStatusConverter = StakingTransactionStatusConverter()
|
||||
private val transactionTypeConverter = StakingTransactionTypeConverter()
|
||||
private val actionStatusConverter = ActionStatusConverter()
|
||||
|
||||
private val transactionConverter = StakingTransactionConverter(
|
||||
transactionStatusConverter = transactionStatusConverter,
|
||||
transactionTypeConverter = transactionTypeConverter,
|
||||
)
|
||||
private val enterActionResponseConverter = EnterActionResponseConverter(
|
||||
actionStatusConverter = actionStatusConverter,
|
||||
transactionConverter = transactionConverter,
|
||||
)
|
||||
|
||||
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 suspend fun fetchYields() {
|
||||
withContext(dispatchers.io) {
|
||||
val yieldsResponses = getAvailableStakeKitIntegrationsIds().map {
|
||||
async { it.getYieldRequest() }
|
||||
}.awaitAll()
|
||||
|
||||
val yields = yieldsResponses.flatMap { response ->
|
||||
when (response) {
|
||||
is ApiResponse.Success -> response.data.data.filter { yield -> yield.isAvailable == true }
|
||||
is ApiResponse.Error -> {
|
||||
Timber.e("Error fetching enabled yields: ${response.cause}")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
stakingYieldsStore.store(yields)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield {
|
||||
return withContext(dispatchers.io) {
|
||||
val rawCurrencyId = cryptoCurrencyId.rawCurrencyId ?: error("Staking custom tokens is not available")
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = getEnabledYieldsSync(),
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = symbol,
|
||||
)
|
||||
|
||||
prefetchedYield ?: error("Staking is unavailable")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getYield(yieldId: String): Yield {
|
||||
return withContext(dispatchers.io) {
|
||||
getEnabledYieldsSync().find { it.id == yieldId } ?: error("Staking is unavailable")
|
||||
}
|
||||
}
|
||||
|
||||
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 = StakingNetworkTypeConverter.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 suspend fun StakingIntegrationID.StakeKit.getYieldRequest(): ApiResponse<EnabledYieldsResponse> {
|
||||
return when (this) {
|
||||
is StakingIntegrationID.StakeKit.Coin -> stakeKitApi.getEnabledYields(
|
||||
preferredValidatorsOnly = false,
|
||||
network = networkId,
|
||||
)
|
||||
is StakingIntegrationID.StakeKit.EthereumToken -> stakeKitApi.getEnabledYields(
|
||||
preferredValidatorsOnly = false,
|
||||
yieldId = value,
|
||||
network = networkId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvailableStakeKitIntegrationsIds(): List<StakingIntegrationID.StakeKit> {
|
||||
return StakingIntegrationID.StakeKit.entries.filterNot {
|
||||
it.blockchain == Blockchain.Cardano && !stakingFeatureToggles.isCardanoStakingEnabled
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
StakingEntryInfo(
|
||||
tokenSymbol = yield.token.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun createAction(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): StakingAction {
|
||||
return withContext(dispatchers.io) {
|
||||
val response = when (params.actionCommonType) {
|
||||
is StakingActionCommonType.Enter -> stakeKitApi.createEnterAction(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Exit -> stakeKitApi.createExitAction(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Pending -> stakeKitApi.createPendingAction(
|
||||
createPendingActionRequestBody(params),
|
||||
)
|
||||
}
|
||||
|
||||
enterActionResponseConverter.convert(response.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun estimateGas(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): StakingGasEstimate {
|
||||
return withContext(dispatchers.io) {
|
||||
val gasEstimateDTO = when (params.actionCommonType) {
|
||||
is StakingActionCommonType.Enter -> stakeKitApi.estimateGasOnEnter(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Exit -> stakeKitApi.estimateGasOnExit(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Pending -> stakeKitApi.estimateGasOnPending(
|
||||
createPendingActionRequestBody(params),
|
||||
)
|
||||
}
|
||||
|
||||
GasEstimateConverter.convert(gasEstimateDTO.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun constructTransaction(
|
||||
networkId: String,
|
||||
fee: Fee,
|
||||
amount: Amount,
|
||||
transactionId: String,
|
||||
): Pair<StakingTransaction, TransactionData.Compiled> {
|
||||
return withContext(dispatchers.io) {
|
||||
val transactionResponse = stakeKitApi.constructTransaction(
|
||||
transactionId = transactionId,
|
||||
body = ConstructTransactionRequestBody(),
|
||||
)
|
||||
|
||||
val transaction = transactionConverter.convert(transactionResponse.getOrThrow())
|
||||
val unsignedTransaction =
|
||||
transaction.unsignedTransaction ?: error("No unsigned transaction available")
|
||||
val transactionData = TransactionData.Compiled(
|
||||
value = getTransactionDataType(networkId, unsignedTransaction),
|
||||
fee = fee,
|
||||
amount = amount,
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
)
|
||||
|
||||
transaction to transactionData
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createActionRequestBody(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): ActionRequestBody {
|
||||
return ActionRequestBody(
|
||||
integrationId = params.integrationId,
|
||||
addresses = Address(
|
||||
address = params.address,
|
||||
additionalAddresses = createAdditionalAddresses(userWalletId, network, params),
|
||||
),
|
||||
args = ActionRequestBodyArgs(
|
||||
amount = params.amount.toPlainString(),
|
||||
inputToken = YieldTokenConverter.convertBack(params.token),
|
||||
validatorAddress = params.validatorAddress,
|
||||
validatorAddresses = listOf(params.validatorAddress), // check on other networks
|
||||
tronResource = getTronResource(network),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPendingActionRequestBody(params: ActionParams): PendingActionRequestBody {
|
||||
return PendingActionRequestBody(
|
||||
integrationId = params.integrationId,
|
||||
type = params.type ?: StakingActionType.UNKNOWN,
|
||||
passthrough = params.passthrough.orEmpty(),
|
||||
args = ActionRequestBodyArgs(
|
||||
amount = params.amount.toPlainString(),
|
||||
validatorAddress = params.validatorAddress,
|
||||
validatorAddresses = listOf(params.validatorAddress),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun createAdditionalAddresses(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): Address.AdditionalAddresses? {
|
||||
val selectedWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
return when (params.token.network) {
|
||||
NetworkType.COSMOS -> Address.AdditionalAddresses(
|
||||
cosmosPubKey = Base64.encodeToString(
|
||||
/* input = */ selectedWallet?.wallet?.publicKey?.blockchainKey?.toCompressedPublicKey(),
|
||||
/* flags = */ Base64.NO_WRAP,
|
||||
),
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTransactionDataType(networkId: String, unsignedTransaction: String): TransactionData.Compiled.Data {
|
||||
return when (Blockchain.fromId(networkId)) {
|
||||
Blockchain.Solana,
|
||||
Blockchain.Cosmos,
|
||||
-> TransactionData.Compiled.Data.Bytes(unsignedTransaction.hexToBytes())
|
||||
Blockchain.BSC,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.TON,
|
||||
Blockchain.Cardano,
|
||||
-> TransactionData.Compiled.Data.RawString(unsignedTransaction)
|
||||
Blockchain.Tron -> {
|
||||
val tronStakeKitTransaction = tronStakeKitTransactionAdapter.fromJson(unsignedTransaction)
|
||||
?: error("Failed to parse Tron StakeKit transaction")
|
||||
TransactionData.Compiled.Data.RawString(tronStakeKitTransaction.rawDataHex)
|
||||
}
|
||||
else -> error("Unsupported blockchain")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: CryptoCurrency.RawID, symbol: String): Yield? {
|
||||
return yields.find { yield ->
|
||||
yield.tokens.any { it.coinGeckoId == currencyId.value && it.symbol == symbol }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getEnabledYieldsSync(): List<Yield> {
|
||||
return YieldConverter.convertListIgnoreErrors(
|
||||
input = stakingYieldsStore.getSync(),
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
}
|
||||
|
||||
override fun getEnabledYields(): Flow<List<Yield>> {
|
||||
return stakingYieldsStore.get().map { yields ->
|
||||
YieldConverter.convertListIgnoreErrors(
|
||||
input = yields,
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStakingAvailability(
|
||||
rawCurrencyId: CryptoCurrency.RawID,
|
||||
symbol: String,
|
||||
): Flow<StakingAvailability> {
|
||||
return getEnabledYields()
|
||||
.distinctUntilChanged()
|
||||
.map { yields ->
|
||||
if (yields.isEmpty()) {
|
||||
return@map StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = yields,
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = symbol,
|
||||
)
|
||||
|
||||
if (prefetchedYield != null) {
|
||||
StakingAvailability.Available(StakingOption.StakeKit(prefetchedYield))
|
||||
} else {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getStakingAvailabilitySync(
|
||||
rawCurrencyId: CryptoCurrency.RawID,
|
||||
symbol: String,
|
||||
): StakingAvailability {
|
||||
val yields = getEnabledYieldsSync()
|
||||
if (yields.isEmpty()) {
|
||||
return StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = yields,
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = symbol,
|
||||
)
|
||||
|
||||
return if (prefetchedYield != null) {
|
||||
StakingAvailability.Available(StakingOption.StakeKit(prefetchedYield))
|
||||
} else {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTronResource(network: Network): TronResource? {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId)
|
||||
|
||||
return if (blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet) {
|
||||
TronResource.ENERGY
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,16 +6,16 @@ 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.domain.staking.repositories.StakeKitTransactionHashRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultStakingTransactionHashRepository(
|
||||
internal class DefaultStakeKitTransactionHashRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : StakingTransactionHashRepository {
|
||||
) : StakeKitTransactionHashRepository {
|
||||
|
||||
override suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||
withContext(dispatchers.io) {
|
||||
|
|
@ -1,204 +1,38 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import android.util.Base64
|
||||
import arrow.core.getOrElse
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toCompressedPublicKey
|
||||
import com.tangem.data.staking.converters.YieldConverter
|
||||
import com.tangem.data.staking.converters.action.ActionStatusConverter
|
||||
import com.tangem.data.staking.converters.action.EnterActionResponseConverter
|
||||
import com.tangem.data.staking.converters.transaction.GasEstimateConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionStatusConverter
|
||||
import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConverter
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
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.EnabledYieldsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO
|
||||
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.P2PEthPoolVaultsStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.datasource.local.token.converter.StakingNetworkTypeConverter
|
||||
import com.tangem.datasource.local.token.converter.YieldTokenConverter
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isWallet2
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.staking.NetworkType
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.action.StakingActionType
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
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.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
|
||||
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.StakingActionCommonType
|
||||
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 com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isCardano
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.channels.ProducerScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
internal class DefaultStakingRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val p2pEthPoolVaultsStore: P2PEthPoolVaultsStore,
|
||||
private val stakeKitRepository: StakeKitRepository,
|
||||
private val p2pEthPoolRepository: P2PEthPoolRepository,
|
||||
private val stakingBalanceStoreV2: YieldsBalancesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
moshi: Moshi,
|
||||
) : StakingRepository {
|
||||
|
||||
private val transactionStatusConverter = StakingTransactionStatusConverter()
|
||||
private val transactionTypeConverter = StakingTransactionTypeConverter()
|
||||
private val actionStatusConverter = ActionStatusConverter()
|
||||
|
||||
private val transactionConverter = StakingTransactionConverter(
|
||||
transactionStatusConverter = transactionStatusConverter,
|
||||
transactionTypeConverter = transactionTypeConverter,
|
||||
)
|
||||
private val enterActionResponseConverter = EnterActionResponseConverter(
|
||||
actionStatusConverter = actionStatusConverter,
|
||||
transactionConverter = transactionConverter,
|
||||
)
|
||||
|
||||
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 suspend fun fetchYields() {
|
||||
withContext(dispatchers.io) {
|
||||
val yieldsResponses = getAvailableStakeKitIntegrationsIds().map {
|
||||
async { it.getYieldRequest() }
|
||||
}.awaitAll()
|
||||
|
||||
val yields = yieldsResponses.flatMap { response ->
|
||||
when (response) {
|
||||
is ApiResponse.Success -> response.data.data.filter { yield -> yield.isAvailable == true }
|
||||
else -> {
|
||||
Timber.e("Error fetching enabled yields: ${(response as? ApiResponse.Error)?.cause}")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
stakingYieldsStore.store(yields)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield {
|
||||
return withContext(dispatchers.io) {
|
||||
val rawCurrencyId = cryptoCurrencyId.rawCurrencyId ?: error("Staking custom tokens is not available")
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = getEnabledYieldsSync(),
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = symbol,
|
||||
)
|
||||
|
||||
prefetchedYield ?: error("Staking is unavailable")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getYield(yieldId: String): Yield {
|
||||
return withContext(dispatchers.io) {
|
||||
getEnabledYieldsSync().find { it.id == yieldId } ?: error("Staking is unavailable")
|
||||
}
|
||||
}
|
||||
|
||||
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 = StakingNetworkTypeConverter.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 suspend fun StakingIntegrationID.StakeKit.getYieldRequest(): ApiResponse<EnabledYieldsResponse> {
|
||||
return when (this) {
|
||||
is StakingIntegrationID.StakeKit.Coin -> stakeKitApi.getEnabledYields(
|
||||
preferredValidatorsOnly = false,
|
||||
network = networkId,
|
||||
)
|
||||
is StakingIntegrationID.StakeKit.EthereumToken -> stakeKitApi.getEnabledYields(
|
||||
preferredValidatorsOnly = false,
|
||||
yieldId = value,
|
||||
network = networkId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvailableStakeKitIntegrationsIds(): List<StakingIntegrationID.StakeKit> {
|
||||
return StakingIntegrationID.StakeKit.entries.filterNot {
|
||||
it.blockchain == Blockchain.Cardano && !stakingFeatureToggles.isCardanoStakingEnabled
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
StakingEntryInfo(
|
||||
tokenSymbol = yield.token.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStakingAvailability(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
@ -222,14 +56,16 @@ internal class DefaultStakingRepository(
|
|||
|
||||
val stakingIntegration = StakingIntegrationID.create(currencyId = cryptoCurrency.id)
|
||||
|
||||
when (stakingIntegration) {
|
||||
is StakingIntegrationID.P2P -> subscribeToP2PStakingAvailability()
|
||||
is StakingIntegrationID.StakeKit -> subscribeToStakeKitStakingAvailability(
|
||||
val availabilityFlow = when (stakingIntegration) {
|
||||
is StakingIntegrationID.P2P -> p2pEthPoolRepository.getStakingAvailability()
|
||||
is StakingIntegrationID.StakeKit -> stakeKitRepository.getStakingAvailability(
|
||||
rawCurrencyId,
|
||||
cryptoCurrency,
|
||||
cryptoCurrency.symbol,
|
||||
)
|
||||
null -> send(StakingAvailability.Unavailable)
|
||||
null -> flowOf(StakingAvailability.Unavailable)
|
||||
}
|
||||
|
||||
availabilityFlow.collect { send(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,39 +90,25 @@ internal class DefaultStakingRepository(
|
|||
?: return StakingAvailability.Unavailable
|
||||
|
||||
return when (stakingIntegration) {
|
||||
is StakingIntegrationID.P2P -> {
|
||||
val vaults = getP2PEthPoolVaultsSync()
|
||||
if (vaults.isEmpty()) {
|
||||
return StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val vault = findP2PEthPoolVault(
|
||||
vaults = vaults,
|
||||
is StakingIntegrationID.P2P -> p2pEthPoolRepository.getStakingAvailabilitySync()
|
||||
is StakingIntegrationID.StakeKit -> stakeKitRepository.getStakingAvailabilitySync(
|
||||
rawCurrencyId,
|
||||
cryptoCurrency.symbol,
|
||||
)
|
||||
|
||||
if (vault != null) {
|
||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
||||
} else {
|
||||
StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
}
|
||||
is StakingIntegrationID.StakeKit -> {
|
||||
val yields = getEnabledYieldsSync()
|
||||
if (yields.isEmpty()) {
|
||||
return StakingAvailability.TemporaryUnavailable
|
||||
}
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = yields,
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean {
|
||||
return withContext(dispatchers.default) {
|
||||
val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false
|
||||
|
||||
when {
|
||||
prefetchedYield != null -> StakingAvailability.Available(StakingOption.StakeKit(prefetchedYield))
|
||||
else -> StakingAvailability.TemporaryUnavailable
|
||||
val hasDataYieldBalance by lazy {
|
||||
balances.any { yieldBalance ->
|
||||
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
|
||||
}
|
||||
}
|
||||
|
||||
balances.isNotEmpty() && hasDataYieldBalance
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,267 +138,6 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun createAction(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): StakingAction {
|
||||
return withContext(dispatchers.io) {
|
||||
val response = when (params.actionCommonType) {
|
||||
is StakingActionCommonType.Enter -> stakeKitApi.createEnterAction(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Exit -> stakeKitApi.createExitAction(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Pending -> stakeKitApi.createPendingAction(
|
||||
createPendingActionRequestBody(params),
|
||||
)
|
||||
}
|
||||
|
||||
enterActionResponseConverter.convert(response.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun estimateGas(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): StakingGasEstimate {
|
||||
return withContext(dispatchers.io) {
|
||||
val gasEstimateDTO = when (params.actionCommonType) {
|
||||
is StakingActionCommonType.Enter -> stakeKitApi.estimateGasOnEnter(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Exit -> stakeKitApi.estimateGasOnExit(
|
||||
createActionRequestBody(
|
||||
userWalletId,
|
||||
network,
|
||||
params,
|
||||
),
|
||||
)
|
||||
is StakingActionCommonType.Pending -> stakeKitApi.estimateGasOnPending(
|
||||
createPendingActionRequestBody(params),
|
||||
)
|
||||
}
|
||||
|
||||
GasEstimateConverter.convert(gasEstimateDTO.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun constructTransaction(
|
||||
networkId: String,
|
||||
fee: Fee,
|
||||
amount: Amount,
|
||||
transactionId: String,
|
||||
): Pair<StakingTransaction, TransactionData.Compiled> {
|
||||
return withContext(dispatchers.io) {
|
||||
val transactionResponse = stakeKitApi.constructTransaction(
|
||||
transactionId = transactionId,
|
||||
body = ConstructTransactionRequestBody(),
|
||||
)
|
||||
|
||||
val transaction = transactionConverter.convert(transactionResponse.getOrThrow())
|
||||
val unsignedTransaction =
|
||||
transaction.unsignedTransaction ?: error("No unsigned transaction available")
|
||||
val transactionData = TransactionData.Compiled(
|
||||
value = getTransactionDataType(networkId, unsignedTransaction),
|
||||
fee = fee,
|
||||
amount = amount,
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
)
|
||||
|
||||
transaction to transactionData
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean {
|
||||
return withContext(dispatchers.default) {
|
||||
val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false
|
||||
|
||||
val hasDataYieldBalance by lazy {
|
||||
balances.any { yieldBalance ->
|
||||
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
|
||||
}
|
||||
}
|
||||
|
||||
balances.isNotEmpty() && hasDataYieldBalance
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createActionRequestBody(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): ActionRequestBody {
|
||||
return ActionRequestBody(
|
||||
integrationId = params.integrationId,
|
||||
addresses = Address(
|
||||
address = params.address,
|
||||
additionalAddresses = createAdditionalAddresses(userWalletId, network, params),
|
||||
),
|
||||
args = ActionRequestBodyArgs(
|
||||
amount = params.amount.toPlainString(),
|
||||
inputToken = YieldTokenConverter.convertBack(params.token),
|
||||
validatorAddress = params.validatorAddress,
|
||||
validatorAddresses = listOf(params.validatorAddress), // check on other networks
|
||||
tronResource = getTronResource(network),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPendingActionRequestBody(params: ActionParams): PendingActionRequestBody {
|
||||
return PendingActionRequestBody(
|
||||
integrationId = params.integrationId,
|
||||
type = params.type ?: StakingActionType.UNKNOWN,
|
||||
passthrough = params.passthrough.orEmpty(),
|
||||
args = ActionRequestBodyArgs(
|
||||
amount = params.amount.toPlainString(),
|
||||
validatorAddress = params.validatorAddress,
|
||||
validatorAddresses = listOf(params.validatorAddress),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun createAdditionalAddresses(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
params: ActionParams,
|
||||
): Address.AdditionalAddresses? {
|
||||
val selectedWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
return when (params.token.network) {
|
||||
NetworkType.COSMOS -> Address.AdditionalAddresses(
|
||||
cosmosPubKey = Base64.encodeToString(
|
||||
/* input = */ selectedWallet?.wallet?.publicKey?.blockchainKey?.toCompressedPublicKey(),
|
||||
/* flags = */ Base64.NO_WRAP,
|
||||
),
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTransactionDataType(networkId: String, unsignedTransaction: String): TransactionData.Compiled.Data {
|
||||
return when (Blockchain.fromId(networkId)) {
|
||||
Blockchain.Solana,
|
||||
Blockchain.Cosmos,
|
||||
-> TransactionData.Compiled.Data.Bytes(unsignedTransaction.hexToBytes())
|
||||
Blockchain.BSC,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.TON,
|
||||
Blockchain.Cardano,
|
||||
-> TransactionData.Compiled.Data.RawString(unsignedTransaction)
|
||||
Blockchain.Tron -> {
|
||||
val tronStakeKitTransaction = tronStakeKitTransactionAdapter.fromJson(unsignedTransaction)
|
||||
?: error("Failed to parse Tron StakeKit transaction")
|
||||
TransactionData.Compiled.Data.RawString(tronStakeKitTransaction.rawDataHex)
|
||||
}
|
||||
else -> error("Unsupported blockchain")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: CryptoCurrency.RawID, symbol: String): Yield? {
|
||||
return yields.find { yield ->
|
||||
yield.tokens.any { it.coinGeckoId == currencyId.value && it.symbol == symbol }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getEnabledYieldsSync(): List<Yield> {
|
||||
return YieldConverter.convertListIgnoreErrors(
|
||||
input = stakingYieldsStore.getSync(),
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
}
|
||||
|
||||
override fun getEnabledYields(): Flow<List<Yield>> {
|
||||
return stakingYieldsStore.get().map {
|
||||
YieldConverter.convertListIgnoreErrors(
|
||||
input = it,
|
||||
onError = { Timber.e("Error converting one of the items in enabled yields: $it") },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getP2PEthPoolVaultsSync(): List<P2PEthPoolVault> {
|
||||
return p2pEthPoolVaultsStore.getSync()
|
||||
}
|
||||
|
||||
private fun getP2PEthPoolVaults(): Flow<List<P2PEthPoolVault>> {
|
||||
return p2pEthPoolVaultsStore.get()
|
||||
}
|
||||
|
||||
private fun findP2PEthPoolVault(vaults: List<P2PEthPoolVault>): P2PEthPoolVault? {
|
||||
return vaults.firstOrNull { vault -> !vault.isPrivate }
|
||||
}
|
||||
|
||||
private fun ProducerScope<StakingAvailability>.subscribeToP2PStakingAvailability() {
|
||||
getP2PEthPoolVaults()
|
||||
.distinctUntilChanged()
|
||||
.onEach { vaults ->
|
||||
if (vaults.isEmpty()) {
|
||||
send(StakingAvailability.TemporaryUnavailable)
|
||||
return@onEach
|
||||
}
|
||||
|
||||
val vault = findP2PEthPoolVault(vaults = vaults)
|
||||
|
||||
if (vault != null) {
|
||||
send(StakingAvailability.Available(StakingOption.P2P(vault)))
|
||||
} else {
|
||||
send(StakingAvailability.TemporaryUnavailable)
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private fun ProducerScope<StakingAvailability>.subscribeToStakeKitStakingAvailability(
|
||||
rawCurrencyId: CryptoCurrency.RawID,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
getEnabledYields()
|
||||
.distinctUntilChanged()
|
||||
.onEach { yields ->
|
||||
if (yields.isEmpty()) {
|
||||
send(StakingAvailability.TemporaryUnavailable)
|
||||
return@onEach
|
||||
}
|
||||
|
||||
val prefetchedYield = findPrefetchedYield(
|
||||
yields = yields,
|
||||
currencyId = rawCurrencyId,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
|
||||
if (prefetchedYield != null) {
|
||||
send(StakingAvailability.Available(StakingOption.StakeKit(prefetchedYield)))
|
||||
} else {
|
||||
send(StakingAvailability.TemporaryUnavailable)
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private fun getTronResource(network: Network): TronResource? {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId)
|
||||
|
||||
return if (blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet) {
|
||||
TronResource.ENERGY
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import com.squareup.moshi.Moshi
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.data.staking.DefaultP2PEthPoolRepository
|
||||
import com.tangem.data.staking.DefaultStakingActionRepository
|
||||
import com.tangem.data.staking.DefaultStakeKitActionRepository
|
||||
import com.tangem.data.staking.DefaultStakingErrorResolver
|
||||
import com.tangem.data.staking.DefaultStakeKitRepository
|
||||
import com.tangem.data.staking.DefaultStakingRepository
|
||||
import com.tangem.data.staking.DefaultStakingTransactionHashRepository
|
||||
import com.tangem.data.staking.DefaultStakeKitTransactionHashRepository
|
||||
import com.tangem.data.staking.converters.error.StakeKitErrorConverter
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles
|
||||
|
|
@ -21,10 +22,11 @@ import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
|
|||
import com.tangem.datasource.local.token.StakingActionsStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||
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.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
|
@ -42,30 +44,44 @@ internal object StakingDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingRepository(
|
||||
fun provideStakeKitRepository(
|
||||
stakeKitApi: StakeKitApi,
|
||||
stakingYieldsStore: StakingYieldsStore,
|
||||
p2pEthPoolVaultsStore: P2PEthPoolVaultsStore,
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
getUserWalletUseCase: GetUserWalletUseCase,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
): StakeKitRepository {
|
||||
return DefaultStakeKitRepository(
|
||||
stakeKitApi = stakeKitApi,
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
p2pEthPoolVaultsStore = p2pEthPoolVaultsStore,
|
||||
stakingBalanceStoreV2 = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
moshi = moshi,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingRepository(
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
p2pEthPoolRepository: P2PEthPoolRepository,
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
getUserWalletUseCase: GetUserWalletUseCase,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
p2pEthPoolRepository = p2pEthPoolRepository,
|
||||
stakingBalanceStoreV2 = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideP2PEthPoolRepository(
|
||||
|
|
@ -86,8 +102,8 @@ internal object StakingDataModule {
|
|||
stakeKitApi: StakeKitApi,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): StakingTransactionHashRepository {
|
||||
return DefaultStakingTransactionHashRepository(
|
||||
): StakeKitTransactionHashRepository {
|
||||
return DefaultStakeKitTransactionHashRepository(
|
||||
stakeKitApi = stakeKitApi,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
dispatchers = dispatchers,
|
||||
|
|
@ -99,8 +115,8 @@ internal object StakingDataModule {
|
|||
fun provideStakingActionRepository(
|
||||
stakingActionsStore: StakingActionsStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): StakingActionRepository {
|
||||
return DefaultStakingActionRepository(
|
||||
): StakeKitActionRepository {
|
||||
return DefaultStakeKitActionRepository(
|
||||
stakingActionsStore = stakingActionsStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
|||
|
||||
fetch(userWalletId = params.userWalletId, stakingIds = availableStakingIds)
|
||||
}
|
||||
.onLeft {
|
||||
Timber.e(it, "Unable to fetch yield balances $params")
|
||||
.onLeft { throwable ->
|
||||
Timber.e(throwable, "Unable to fetch yield balances $params")
|
||||
|
||||
yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds)
|
||||
}
|
||||
|
|
@ -163,21 +163,21 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
|||
|
||||
if (!allResponsesReceived(requests, yieldBalances)) {
|
||||
val values = stakingIds.filter { stakingId ->
|
||||
yieldBalances.none {
|
||||
stakingId.integrationId == it.integrationId &&
|
||||
stakingId.address == it.addresses.address
|
||||
yieldBalances.none { balanceWrapper ->
|
||||
stakingId.integrationId == balanceWrapper.integrationId &&
|
||||
stakingId.address == balanceWrapper.addresses.address
|
||||
}
|
||||
}
|
||||
|
||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = values.toSet())
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
Timber.e(it, "Unable to fetch yield balances $userWalletId")
|
||||
onError = { throwable ->
|
||||
Timber.e(throwable, "Unable to fetch yield balances $userWalletId")
|
||||
|
||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||
|
||||
throw it
|
||||
throw throwable
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -187,9 +187,9 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
|||
yieldBalances: Set<YieldBalanceWrapperDTO>,
|
||||
): Boolean {
|
||||
return requests.all { request ->
|
||||
yieldBalances.any {
|
||||
request.integrationId == it.integrationId &&
|
||||
request.addresses.address == it.addresses.address
|
||||
yieldBalances.any { balance ->
|
||||
request.integrationId == balance.integrationId &&
|
||||
request.addresses.address == balance.addresses.address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ internal object YieldBalanceRequestBodyFactory {
|
|||
return YieldBalanceRequestBody(
|
||||
addresses = YieldBalanceRequestBodyAddressFactory.create(stakingID),
|
||||
args = YieldBalanceRequestBody.YieldBalanceRequestArgs(
|
||||
validatorAddresses = listOf(), // todo add validators [REDACTED_JIRA]
|
||||
validatorAddresses = emptyList(), // todo add validators [REDACTED_JIRA]
|
||||
),
|
||||
integrationId = stakingID.integrationId,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ features/staking/impl 56
|
|||
data/wallet-connect 55
|
||||
features/swap-v2/impl 53
|
||||
features/walletconnect/impl 51
|
||||
features/tokendetails/impl 50
|
||||
features/tokendetails/impl 49
|
||||
features/manage-tokens/impl 45
|
||||
domain/wallets 39
|
||||
features/nft/impl 36
|
||||
|
|
@ -65,7 +65,6 @@ features/details/impl 10
|
|||
data/markets 10
|
||||
domain/staking 9
|
||||
data/yield-supply 9
|
||||
data/staking 9
|
||||
data/networks 9
|
||||
features/welcome/impl 8
|
||||
features/home/impl 8
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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>)
|
||||
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ internalUiState.value = stateFactory.getStateWithErrorDialog(stringReference(it)) Timber.e(it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ internalUiState.value = stateFactory.getStateWithUpdatedHidden( isBalanceHidden = it.isBalanceHidden, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ sendButtonsEvents(it.states) internalUiState.value = stateFactory.getManageButtonsState(actions = it.states) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ val stakingEntryInfo = if (it is StakingAvailability.Available) { getStakingEntryInfoUseCase( cryptoCurrencyId = cryptoCurrency.id, symbol = cryptoCurrency.symbol, ).getOrNull() } else { null } internalUiState.update { state -> stateFactory.getStakingInfoState( state = state, stakingEntryInfo = stakingEntryInfo, stakingAvailability = it, cryptoCurrencyStatus = cryptoCurrencyStatus, ) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ val updatedState = stateFactory.getStateWithNotifications(it) notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications) internalUiState.value = updatedState }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsOnrampTransactionStateConverter.kt$TokenDetailsOnrampTransactionStateConverter${ analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider) clickIntents.onGoToProviderClick(it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenDetailsScreen.kt${ Notification( modifier = itemModifier.animateItem(), config = it.config, iconTint = when (it) { is TokenDetailsNotification.Informational -> TangemTheme.colors.icon.accent is TokenDetailsNotification.UsedOutdatedData -> TangemTheme.colors.text.attention else -> null }, ) }</ID>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue