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.*
|
||||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
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.StakingErrorResolver
|
||||||
|
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||||
import com.tangem.domain.staking.repositories.StakingRepository
|
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.single.SingleYieldBalanceFetcher
|
||||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -35,11 +36,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetStakingEntryInfoUseCase(
|
fun provideGetStakingEntryInfoUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): GetStakingEntryInfoUseCase {
|
): GetStakingEntryInfoUseCase {
|
||||||
return GetStakingEntryInfoUseCase(
|
return GetStakingEntryInfoUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -47,11 +48,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetYieldUseCase(
|
fun provideGetYieldUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): GetYieldUseCase {
|
): GetYieldUseCase {
|
||||||
return GetYieldUseCase(
|
return GetYieldUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -59,13 +60,13 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideFetchActionsUseCase(
|
fun provideFetchActionsUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingActionRepository: StakingActionRepository,
|
stakeKitActionRepository: StakeKitActionRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): FetchActionsUseCase {
|
): FetchActionsUseCase {
|
||||||
return FetchActionsUseCase(
|
return FetchActionsUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingActionRepository = stakingActionRepository,
|
stakeKitActionRepository = stakeKitActionRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -73,11 +74,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetActionsUseCase(
|
fun provideGetActionsUseCase(
|
||||||
stakingActionRepository: StakingActionRepository,
|
stakeKitActionRepository: StakeKitActionRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): GetActionsUseCase {
|
): GetActionsUseCase {
|
||||||
return GetActionsUseCase(
|
return GetActionsUseCase(
|
||||||
stakingActionRepository = stakingActionRepository,
|
stakeKitActionRepository = stakeKitActionRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -85,11 +86,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetStakingTokensUseCase(
|
fun provideGetStakingTokensUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): FetchStakingTokensUseCase {
|
): FetchStakingTokensUseCase {
|
||||||
return FetchStakingTokensUseCase(
|
return FetchStakingTokensUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -97,12 +98,12 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideFetchStakingOptionsUseCase(
|
fun provideFetchStakingOptionsUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
p2pRepository: P2PEthPoolRepository,
|
p2pRepository: P2PEthPoolRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): FetchStakingOptionsUseCase {
|
): FetchStakingOptionsUseCase {
|
||||||
return FetchStakingOptionsUseCase(
|
return FetchStakingOptionsUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
p2pRepository = p2pRepository,
|
p2pRepository = p2pRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
|
|
@ -123,11 +124,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetStakingTransactionsUseCase(
|
fun provideGetStakingTransactionsUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): GetStakingTransactionsUseCase {
|
): GetStakingTransactionsUseCase {
|
||||||
return GetStakingTransactionsUseCase(
|
return GetStakingTransactionsUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -135,11 +136,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGasEstimateUseCase(
|
fun provideGasEstimateUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): EstimateGasUseCase {
|
): EstimateGasUseCase {
|
||||||
return EstimateGasUseCase(
|
return EstimateGasUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -147,11 +148,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSubmitHashUseCase(
|
fun provideSubmitHashUseCase(
|
||||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): SubmitHashUseCase {
|
): SubmitHashUseCase {
|
||||||
return SubmitHashUseCase(
|
return SubmitHashUseCase(
|
||||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -159,11 +160,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSaveUnsubmittedHashUseCase(
|
fun provideSaveUnsubmittedHashUseCase(
|
||||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): SaveUnsubmittedHashUseCase {
|
): SaveUnsubmittedHashUseCase {
|
||||||
return SaveUnsubmittedHashUseCase(
|
return SaveUnsubmittedHashUseCase(
|
||||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -181,11 +182,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSendUnsubmittedHashesUseCase(
|
fun provideSendUnsubmittedHashesUseCase(
|
||||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): SendUnsubmittedHashesUseCase {
|
): SendUnsubmittedHashesUseCase {
|
||||||
return SendUnsubmittedHashesUseCase(
|
return SendUnsubmittedHashesUseCase(
|
||||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -193,11 +194,11 @@ internal object StakingDomainModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetConstructedStakingTransactionUseCase(
|
fun provideGetConstructedStakingTransactionUseCase(
|
||||||
stakingRepository: StakingRepository,
|
stakeKitRepository: StakeKitRepository,
|
||||||
stakingErrorResolver: StakingErrorResolver,
|
stakingErrorResolver: StakingErrorResolver,
|
||||||
): GetConstructedStakingTransactionUseCase {
|
): GetConstructedStakingTransactionUseCase {
|
||||||
return GetConstructedStakingTransactionUseCase(
|
return GetConstructedStakingTransactionUseCase(
|
||||||
stakingRepository = stakingRepository,
|
stakeKitRepository = stakeKitRepository,
|
||||||
stakingErrorResolver = stakingErrorResolver,
|
stakingErrorResolver = stakingErrorResolver,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +237,7 @@ internal object StakingDomainModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideStakingApyFlowUseCase(stakingRepository: StakingRepository): StakingApyFlowUseCase {
|
fun provideStakingApyFlowUseCase(stakeKitRepository: StakeKitRepository): StakingApyFlowUseCase {
|
||||||
return StakingApyFlowUseCase(stakingRepository)
|
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.P2PEthPoolUnstakeRequest
|
||||||
import com.tangem.datasource.api.ethpool.models.request.P2PEthPoolWithdrawRequest
|
import com.tangem.datasource.api.ethpool.models.request.P2PEthPoolWithdrawRequest
|
||||||
import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
|
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.model.ethpool.*
|
||||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
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 kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
@ -192,7 +197,12 @@ internal class DefaultP2PEthPoolRepository(
|
||||||
period: Int?,
|
period: Int?,
|
||||||
): Either<StakingError, List<P2PEthPoolReward>> = either {
|
): Either<StakingError, List<P2PEthPoolReward>> = either {
|
||||||
withContext(dispatchers.io) {
|
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) {
|
when (response) {
|
||||||
is ApiResponse.Success -> {
|
is ApiResponse.Success -> {
|
||||||
val data = response.data
|
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.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
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 com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
internal class DefaultStakingActionRepository(
|
internal class DefaultStakeKitActionRepository(
|
||||||
private val stakingActionsStore: StakingActionsStore,
|
private val stakingActionsStore: StakingActionsStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : StakingActionRepository {
|
) : StakeKitActionRepository {
|
||||||
|
|
||||||
override suspend fun store(
|
override suspend fun store(
|
||||||
userWalletId: UserWalletId,
|
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.PreferencesKeys
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||||
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
|
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 com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class DefaultStakingTransactionHashRepository(
|
internal class DefaultStakeKitTransactionHashRepository(
|
||||||
private val stakeKitApi: StakeKitApi,
|
private val stakeKitApi: StakeKitApi,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : StakingTransactionHashRepository {
|
) : StakeKitTransactionHashRepository {
|
||||||
|
|
||||||
override suspend fun submitHash(transactionId: String, transactionHash: String) {
|
override suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
|
|
@ -1,204 +1,38 @@
|
||||||
package com.tangem.data.staking
|
package com.tangem.data.staking
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import arrow.core.getOrElse
|
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.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.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.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.card.common.TapWorkarounds.isWallet2
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
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.YieldBalance
|
||||||
import com.tangem.domain.models.staking.action.StakingActionType
|
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.staking.model.StakingAvailability
|
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.StakingIntegrationID
|
||||||
import com.tangem.domain.staking.model.StakingOption
|
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||||
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
|
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||||
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.StakingRepository
|
import com.tangem.domain.staking.repositories.StakingRepository
|
||||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
|
||||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||||
import com.tangem.lib.crypto.BlockchainUtils.isCardano
|
import com.tangem.lib.crypto.BlockchainUtils.isCardano
|
||||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
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.flow.*
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||||
internal class DefaultStakingRepository(
|
internal class DefaultStakingRepository(
|
||||||
private val stakeKitApi: StakeKitApi,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingYieldsStore: StakingYieldsStore,
|
private val p2pEthPoolRepository: P2PEthPoolRepository,
|
||||||
private val p2pEthPoolVaultsStore: P2PEthPoolVaultsStore,
|
|
||||||
private val stakingBalanceStoreV2: YieldsBalancesStore,
|
private val stakingBalanceStoreV2: YieldsBalancesStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
|
||||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||||
moshi: Moshi,
|
|
||||||
) : StakingRepository {
|
) : 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(
|
override fun getStakingAvailability(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
|
@ -222,14 +56,16 @@ internal class DefaultStakingRepository(
|
||||||
|
|
||||||
val stakingIntegration = StakingIntegrationID.create(currencyId = cryptoCurrency.id)
|
val stakingIntegration = StakingIntegrationID.create(currencyId = cryptoCurrency.id)
|
||||||
|
|
||||||
when (stakingIntegration) {
|
val availabilityFlow = when (stakingIntegration) {
|
||||||
is StakingIntegrationID.P2P -> subscribeToP2PStakingAvailability()
|
is StakingIntegrationID.P2P -> p2pEthPoolRepository.getStakingAvailability()
|
||||||
is StakingIntegrationID.StakeKit -> subscribeToStakeKitStakingAvailability(
|
is StakingIntegrationID.StakeKit -> stakeKitRepository.getStakingAvailability(
|
||||||
rawCurrencyId,
|
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 StakingAvailability.Unavailable
|
||||||
|
|
||||||
return when (stakingIntegration) {
|
return when (stakingIntegration) {
|
||||||
is StakingIntegrationID.P2P -> {
|
is StakingIntegrationID.P2P -> p2pEthPoolRepository.getStakingAvailabilitySync()
|
||||||
val vaults = getP2PEthPoolVaultsSync()
|
is StakingIntegrationID.StakeKit -> stakeKitRepository.getStakingAvailabilitySync(
|
||||||
if (vaults.isEmpty()) {
|
rawCurrencyId,
|
||||||
return StakingAvailability.TemporaryUnavailable
|
cryptoCurrency.symbol,
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val vault = findP2PEthPoolVault(
|
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean {
|
||||||
vaults = vaults,
|
return withContext(dispatchers.default) {
|
||||||
)
|
val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false
|
||||||
|
|
||||||
if (vault != null) {
|
val hasDataYieldBalance by lazy {
|
||||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
balances.any { yieldBalance ->
|
||||||
} else {
|
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
|
||||||
StakingAvailability.TemporaryUnavailable
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is StakingIntegrationID.StakeKit -> {
|
|
||||||
val yields = getEnabledYieldsSync()
|
|
||||||
if (yields.isEmpty()) {
|
|
||||||
return StakingAvailability.TemporaryUnavailable
|
|
||||||
}
|
|
||||||
|
|
||||||
val prefetchedYield = findPrefetchedYield(
|
balances.isNotEmpty() && hasDataYieldBalance
|
||||||
yields = yields,
|
|
||||||
currencyId = rawCurrencyId,
|
|
||||||
symbol = cryptoCurrency.symbol,
|
|
||||||
)
|
|
||||||
|
|
||||||
when {
|
|
||||||
prefetchedYield != null -> StakingAvailability.Available(StakingOption.StakeKit(prefetchedYield))
|
|
||||||
else -> StakingAvailability.TemporaryUnavailable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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 {
|
companion object {
|
||||||
private val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79")
|
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.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||||
import com.tangem.data.staking.DefaultP2PEthPoolRepository
|
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.DefaultStakingErrorResolver
|
||||||
|
import com.tangem.data.staking.DefaultStakeKitRepository
|
||||||
import com.tangem.data.staking.DefaultStakingRepository
|
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.converters.error.StakeKitErrorConverter
|
||||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||||
import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles
|
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.StakingActionsStore
|
||||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
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.StakingErrorResolver
|
||||||
|
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||||
import com.tangem.domain.staking.repositories.StakingRepository
|
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.toggles.StakingFeatureToggles
|
||||||
import com.tangem.domain.staking.utils.StakingCleaner
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -42,30 +44,44 @@ internal object StakingDataModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideStakingRepository(
|
fun provideStakeKitRepository(
|
||||||
stakeKitApi: StakeKitApi,
|
stakeKitApi: StakeKitApi,
|
||||||
stakingYieldsStore: StakingYieldsStore,
|
stakingYieldsStore: StakingYieldsStore,
|
||||||
p2pEthPoolVaultsStore: P2PEthPoolVaultsStore,
|
|
||||||
yieldsBalancesStore: YieldsBalancesStore,
|
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
walletManagersFacade: WalletManagersFacade,
|
walletManagersFacade: WalletManagersFacade,
|
||||||
getUserWalletUseCase: GetUserWalletUseCase,
|
|
||||||
stakingFeatureToggles: StakingFeatureToggles,
|
stakingFeatureToggles: StakingFeatureToggles,
|
||||||
@NetworkMoshi moshi: Moshi,
|
@NetworkMoshi moshi: Moshi,
|
||||||
): StakingRepository {
|
): StakeKitRepository {
|
||||||
return DefaultStakingRepository(
|
return DefaultStakeKitRepository(
|
||||||
stakeKitApi = stakeKitApi,
|
stakeKitApi = stakeKitApi,
|
||||||
stakingYieldsStore = stakingYieldsStore,
|
stakingYieldsStore = stakingYieldsStore,
|
||||||
p2pEthPoolVaultsStore = p2pEthPoolVaultsStore,
|
|
||||||
stakingBalanceStoreV2 = yieldsBalancesStore,
|
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
getUserWalletUseCase = getUserWalletUseCase,
|
|
||||||
stakingFeatureToggles = stakingFeatureToggles,
|
stakingFeatureToggles = stakingFeatureToggles,
|
||||||
moshi = moshi,
|
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
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideP2PEthPoolRepository(
|
fun provideP2PEthPoolRepository(
|
||||||
|
|
@ -86,8 +102,8 @@ internal object StakingDataModule {
|
||||||
stakeKitApi: StakeKitApi,
|
stakeKitApi: StakeKitApi,
|
||||||
appPreferencesStore: AppPreferencesStore,
|
appPreferencesStore: AppPreferencesStore,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): StakingTransactionHashRepository {
|
): StakeKitTransactionHashRepository {
|
||||||
return DefaultStakingTransactionHashRepository(
|
return DefaultStakeKitTransactionHashRepository(
|
||||||
stakeKitApi = stakeKitApi,
|
stakeKitApi = stakeKitApi,
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
|
@ -99,8 +115,8 @@ internal object StakingDataModule {
|
||||||
fun provideStakingActionRepository(
|
fun provideStakingActionRepository(
|
||||||
stakingActionsStore: StakingActionsStore,
|
stakingActionsStore: StakingActionsStore,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): StakingActionRepository {
|
): StakeKitActionRepository {
|
||||||
return DefaultStakingActionRepository(
|
return DefaultStakeKitActionRepository(
|
||||||
stakingActionsStore = stakingActionsStore,
|
stakingActionsStore = stakingActionsStore,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
||||||
|
|
||||||
fetch(userWalletId = params.userWalletId, stakingIds = availableStakingIds)
|
fetch(userWalletId = params.userWalletId, stakingIds = availableStakingIds)
|
||||||
}
|
}
|
||||||
.onLeft {
|
.onLeft { throwable ->
|
||||||
Timber.e(it, "Unable to fetch yield balances $params")
|
Timber.e(throwable, "Unable to fetch yield balances $params")
|
||||||
|
|
||||||
yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds)
|
yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds)
|
||||||
}
|
}
|
||||||
|
|
@ -163,21 +163,21 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
||||||
|
|
||||||
if (!allResponsesReceived(requests, yieldBalances)) {
|
if (!allResponsesReceived(requests, yieldBalances)) {
|
||||||
val values = stakingIds.filter { stakingId ->
|
val values = stakingIds.filter { stakingId ->
|
||||||
yieldBalances.none {
|
yieldBalances.none { balanceWrapper ->
|
||||||
stakingId.integrationId == it.integrationId &&
|
stakingId.integrationId == balanceWrapper.integrationId &&
|
||||||
stakingId.address == it.addresses.address
|
stakingId.address == balanceWrapper.addresses.address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = values.toSet())
|
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = values.toSet())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError = {
|
onError = { throwable ->
|
||||||
Timber.e(it, "Unable to fetch yield balances $userWalletId")
|
Timber.e(throwable, "Unable to fetch yield balances $userWalletId")
|
||||||
|
|
||||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds)
|
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
|
||||||
throw it
|
throw throwable
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -187,9 +187,9 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor(
|
||||||
yieldBalances: Set<YieldBalanceWrapperDTO>,
|
yieldBalances: Set<YieldBalanceWrapperDTO>,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return requests.all { request ->
|
return requests.all { request ->
|
||||||
yieldBalances.any {
|
yieldBalances.any { balance ->
|
||||||
request.integrationId == it.integrationId &&
|
request.integrationId == balance.integrationId &&
|
||||||
request.addresses.address == it.addresses.address
|
request.addresses.address == balance.addresses.address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ internal object YieldBalanceRequestBodyFactory {
|
||||||
return YieldBalanceRequestBody(
|
return YieldBalanceRequestBody(
|
||||||
addresses = YieldBalanceRequestBodyAddressFactory.create(stakingID),
|
addresses = YieldBalanceRequestBodyAddressFactory.create(stakingID),
|
||||||
args = YieldBalanceRequestBody.YieldBalanceRequestArgs(
|
args = YieldBalanceRequestBody.YieldBalanceRequestArgs(
|
||||||
validatorAddresses = listOf(), // todo add validators [REDACTED_JIRA]
|
validatorAddresses = emptyList(), // todo add validators [REDACTED_JIRA]
|
||||||
),
|
),
|
||||||
integrationId = stakingID.integrationId,
|
integrationId = stakingID.integrationId,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ features/staking/impl 56
|
||||||
data/wallet-connect 55
|
data/wallet-connect 55
|
||||||
features/swap-v2/impl 53
|
features/swap-v2/impl 53
|
||||||
features/walletconnect/impl 51
|
features/walletconnect/impl 51
|
||||||
features/tokendetails/impl 50
|
features/tokendetails/impl 49
|
||||||
features/manage-tokens/impl 45
|
features/manage-tokens/impl 45
|
||||||
domain/wallets 39
|
domain/wallets 39
|
||||||
features/nft/impl 36
|
features/nft/impl 36
|
||||||
|
|
@ -65,7 +65,6 @@ features/details/impl 10
|
||||||
data/markets 10
|
data/markets 10
|
||||||
domain/staking 9
|
domain/staking 9
|
||||||
data/yield-supply 9
|
data/yield-supply 9
|
||||||
data/staking 9
|
|
||||||
data/networks 9
|
data/networks 9
|
||||||
features/welcome/impl 8
|
features/welcome/impl 8
|
||||||
features/home/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.ActionParams
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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 com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use case for staking gas estimation.
|
* Use case for staking gas estimation.
|
||||||
*/
|
*/
|
||||||
class EstimateGasUseCase(
|
class EstimateGasUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ class EstimateGasUseCase(
|
||||||
params: ActionParams,
|
params: ActionParams,
|
||||||
): Either<StakingError, StakingGasEstimate> {
|
): Either<StakingError, StakingGasEstimate> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
stakingRepository.estimateGas(userWalletId, network, params)
|
stakeKitRepository.estimateGas(userWalletId, network, params)
|
||||||
}.mapLeft {
|
}.mapLeft {
|
||||||
stakingErrorResolver.resolve(it)
|
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.models.staking.NetworkType
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
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.StakingErrorResolver
|
||||||
import com.tangem.domain.staking.repositories.StakingRepository
|
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use case for getting pending actions list.
|
* Use case for getting pending actions list.
|
||||||
*/
|
*/
|
||||||
class FetchActionsUseCase(
|
class FetchActionsUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingActionRepository: StakingActionRepository,
|
private val stakeKitActionRepository: StakeKitActionRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -27,14 +27,14 @@ class FetchActionsUseCase(
|
||||||
): Either<StakingError, Unit> {
|
): Either<StakingError, Unit> {
|
||||||
return Either
|
return Either
|
||||||
.catch {
|
.catch {
|
||||||
val actions = stakingRepository.getActions(
|
val actions = stakeKitRepository.getActions(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
networkType = networkType,
|
networkType = networkType,
|
||||||
stakingActionStatus = stakingActionStatus,
|
stakingActionStatus = stakingActionStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
stakingActionRepository.store(userWalletId, cryptoCurrency.id, actions)
|
stakeKitActionRepository.store(userWalletId, cryptoCurrency.id, actions)
|
||||||
}
|
}
|
||||||
.mapLeft { stakingErrorResolver.resolve(it) }
|
.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.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
|
||||||
* Fetches both StakeKit yields and P2P vaults
|
* Fetches both StakeKit yields and P2P vaults
|
||||||
*/
|
*/
|
||||||
class FetchStakingOptionsUseCase(
|
class FetchStakingOptionsUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val p2pRepository: P2PEthPoolRepository,
|
private val p2pRepository: P2PEthPoolRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
@ -24,7 +24,7 @@ class FetchStakingOptionsUseCase(
|
||||||
catch(
|
catch(
|
||||||
block = {
|
block = {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch { stakingRepository.fetchYields() }
|
launch { stakeKitRepository.fetchYields() }
|
||||||
launch { p2pRepository.fetchVaults() }
|
launch { p2pRepository.fetchVaults() }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,19 @@ import arrow.core.raise.catch
|
||||||
import arrow.core.raise.either
|
import arrow.core.raise.either
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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
|
* Use case for getting enabled tokens
|
||||||
*/
|
*/
|
||||||
class FetchStakingTokensUseCase(
|
class FetchStakingTokensUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(): Either<StakingError, Unit> {
|
suspend operator fun invoke(): Either<StakingError, Unit> {
|
||||||
return either {
|
return either {
|
||||||
catch(
|
catch(
|
||||||
block = { stakingRepository.fetchYields() },
|
block = { stakeKitRepository.fetchYields() },
|
||||||
catch = { stakingErrorResolver.resolve(it) },
|
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.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
import com.tangem.domain.staking.model.stakekit.action.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.staking.repositories.StakingErrorResolver
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.map
|
||||||
* Use case for getting pending actions list.
|
* Use case for getting pending actions list.
|
||||||
*/
|
*/
|
||||||
class GetActionsUseCase(
|
class GetActionsUseCase(
|
||||||
private val stakingActionRepository: StakingActionRepository,
|
private val stakeKitActionRepository: StakeKitActionRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ class GetActionsUseCase(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
cryptoCurrencyId: CryptoCurrency.ID,
|
cryptoCurrencyId: CryptoCurrency.ID,
|
||||||
): EitherFlow<StakingError, List<StakingAction>> {
|
): EitherFlow<StakingError, List<StakingAction>> {
|
||||||
return stakingActionRepository.get(
|
return stakeKitActionRepository.get(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
cryptoCurrencyId = cryptoCurrencyId,
|
cryptoCurrencyId = cryptoCurrencyId,
|
||||||
).map<List<StakingAction>, Either<StakingError, List<StakingAction>>> { it.right() }
|
).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.StakingError
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||||
import com.tangem.domain.staking.repositories.StakingRepository
|
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||||
|
|
||||||
class GetConstructedStakingTransactionUseCase(
|
class GetConstructedStakingTransactionUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ class GetConstructedStakingTransactionUseCase(
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
transactionId: String,
|
transactionId: String,
|
||||||
): Either<StakingError, Pair<StakingTransaction, TransactionData.Compiled>> = Either.catch {
|
): Either<StakingError, Pair<StakingTransaction, TransactionData.Compiled>> = Either.catch {
|
||||||
stakingRepository.constructTransaction(networkId, fee, amount, transactionId)
|
stakeKitRepository.constructTransaction(networkId, fee, amount, transactionId)
|
||||||
}.mapLeft {
|
}.mapLeft {
|
||||||
stakingErrorResolver.resolve(it)
|
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.StakingAvailability
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.staking.repositories.StakingRepository
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.map
|
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.StakingOption
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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.
|
* Use case for getting entry info about staking on token screen.
|
||||||
*/
|
*/
|
||||||
class GetStakingEntryInfoUseCase(
|
class GetStakingEntryInfoUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ class GetStakingEntryInfoUseCase(
|
||||||
.catch {
|
.catch {
|
||||||
when (stakingOption) {
|
when (stakingOption) {
|
||||||
is StakingOption.StakeKit -> {
|
is StakingOption.StakeKit -> {
|
||||||
stakingRepository.getEntryInfo(
|
stakeKitRepository.getEntryInfo(
|
||||||
cryptoCurrencyId = cryptoCurrencyId,
|
cryptoCurrencyId = cryptoCurrencyId,
|
||||||
symbol = symbol,
|
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.ActionParams
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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 com.tangem.domain.models.wallet.UserWalletId
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ import kotlinx.coroutines.delay
|
||||||
* Use case for creating enter action
|
* Use case for creating enter action
|
||||||
*/
|
*/
|
||||||
class GetStakingTransactionsUseCase(
|
class GetStakingTransactionsUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ class GetStakingTransactionsUseCase(
|
||||||
params: ActionParams,
|
params: ActionParams,
|
||||||
): Either<StakingError, List<StakingTransaction>> {
|
): Either<StakingError, List<StakingTransaction>> {
|
||||||
return Either.catch {
|
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
|
// workaround, sometimes transaction is not created immediately after actions/enter
|
||||||
delay(PATCH_TRANSACTION_REQUEST_DELAY)
|
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.StakingError
|
||||||
import com.tangem.domain.staking.model.stakekit.Yield
|
import com.tangem.domain.staking.model.stakekit.Yield
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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.
|
* Use case for getting staking yield for staking scenario start.
|
||||||
*/
|
*/
|
||||||
class GetYieldUseCase(
|
class GetYieldUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
private val stakeKitRepository: StakeKitRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Either<StakingError, Yield> {
|
suspend operator fun invoke(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Either<StakingError, Yield> {
|
||||||
return Either
|
return Either
|
||||||
.catch { stakingRepository.getYield(cryptoCurrencyId, symbol) }
|
.catch { stakeKitRepository.getYield(cryptoCurrencyId, symbol) }
|
||||||
.mapLeft { stakingErrorResolver.resolve(it) }
|
.mapLeft { stakingErrorResolver.resolve(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend operator fun invoke(yieldId: String): Either<StakingError, Yield> {
|
suspend operator fun invoke(yieldId: String): Either<StakingError, Yield> {
|
||||||
return Either
|
return Either
|
||||||
.catch { stakingRepository.getYield(yieldId) }
|
.catch { stakeKitRepository.getYield(yieldId) }
|
||||||
.mapLeft { stakingErrorResolver.resolve(it) }
|
.mapLeft { stakingErrorResolver.resolve(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,8 +3,8 @@ package com.tangem.domain.staking
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.staking.repositories.StakingRepository
|
||||||
|
|
||||||
class IsAnyTokenStakedUseCase(
|
class IsAnyTokenStakedUseCase(
|
||||||
private val stakingRepository: StakingRepository,
|
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.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
|
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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
|
* Use case for saving hash that failed to submit during staking confirmation
|
||||||
*/
|
*/
|
||||||
class SaveUnsubmittedHashUseCase(
|
class SaveUnsubmittedHashUseCase(
|
||||||
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
|
private val stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(transactionId: String, transactionHash: String): Either<StakingError, Unit> {
|
suspend operator fun invoke(transactionId: String, transactionHash: String): Either<StakingError, Unit> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
stakingTransactionHashRepository.storeUnsubmittedHash(
|
stakeKitTransactionHashRepository.storeUnsubmittedHash(
|
||||||
unsubmittedTransactionMetadata = UnsubmittedTransactionMetadata(
|
unsubmittedTransactionMetadata = UnsubmittedTransactionMetadata(
|
||||||
transactionId = transactionId,
|
transactionId = transactionId,
|
||||||
transactionHash = transactionHash,
|
transactionHash = transactionHash,
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,19 @@ package com.tangem.domain.staking
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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
|
* Use case for commiting hashes that failed to submit during staking confirmation
|
||||||
*/
|
*/
|
||||||
class SendUnsubmittedHashesUseCase(
|
class SendUnsubmittedHashesUseCase(
|
||||||
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
|
private val stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(): Either<StakingError, Unit> {
|
suspend operator fun invoke(): Either<StakingError, Unit> {
|
||||||
return Either
|
return Either
|
||||||
.catch { stakingTransactionHashRepository.sendUnsubmittedHashes() }
|
.catch { stakeKitTransactionHashRepository.sendUnsubmittedHashes() }
|
||||||
.mapLeft { stakingErrorResolver.resolve(it) }
|
.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.SubmitHashData
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
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
|
* Use case for submitting transaction hash to stakekit
|
||||||
*/
|
*/
|
||||||
class SubmitHashUseCase(
|
class SubmitHashUseCase(
|
||||||
private val stakingTransactionHashRepository: StakingTransactionHashRepository,
|
private val stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||||
private val stakingErrorResolver: StakingErrorResolver,
|
private val stakingErrorResolver: StakingErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(submitHashData: SubmitHashData): Either<StakingError, Unit> {
|
suspend operator fun invoke(submitHashData: SubmitHashData): Either<StakingError, Unit> {
|
||||||
return Either
|
return Either
|
||||||
.catch {
|
.catch {
|
||||||
stakingTransactionHashRepository.submitHash(
|
stakeKitTransactionHashRepository.submitHash(
|
||||||
transactionId = submitHashData.transactionId,
|
transactionId = submitHashData.transactionId,
|
||||||
transactionHash = submitHashData.transactionHash,
|
transactionHash = submitHashData.transactionHash,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.tangem.domain.staking.repositories
|
package com.tangem.domain.staking.repositories
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
|
import com.tangem.domain.staking.model.StakingAvailability
|
||||||
import com.tangem.domain.staking.model.ethpool.*
|
import com.tangem.domain.staking.model.ethpool.*
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface P2PEthPoolRepository {
|
interface P2PEthPoolRepository {
|
||||||
|
|
||||||
|
|
@ -113,4 +115,20 @@ interface P2PEthPoolRepository {
|
||||||
vaultAddress: String,
|
vaultAddress: String,
|
||||||
period: Int? = null,
|
period: Int? = null,
|
||||||
): Either<StakingError, List<P2PEthPoolReward>>
|
): 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 com.tangem.domain.models.wallet.UserWalletId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface StakingActionRepository {
|
interface StakeKitActionRepository {
|
||||||
|
|
||||||
suspend fun store(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, actions: List<StakingAction>)
|
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
|
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
|
||||||
|
|
||||||
interface StakingTransactionHashRepository {
|
interface StakeKitTransactionHashRepository {
|
||||||
|
|
||||||
suspend fun submitHash(transactionId: String, transactionHash: String)
|
suspend fun submitHash(transactionId: String, transactionHash: String)
|
||||||
|
|
||||||
|
|
@ -1,36 +1,12 @@
|
||||||
package com.tangem.domain.staking.repositories
|
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.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.staking.model.StakingAvailability
|
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
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
// TODO p2p make 3 repos: stakekit, p2p, common staking
|
|
||||||
@Suppress("TooManyFunctions")
|
|
||||||
interface StakingRepository {
|
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>
|
fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow<StakingAvailability>
|
||||||
|
|
||||||
suspend fun getStakingAvailabilitySync(
|
suspend fun getStakingAvailabilitySync(
|
||||||
|
|
@ -38,23 +14,5 @@ interface StakingRepository {
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
): StakingAvailability
|
): 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
|
suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.domain.staking.usecase
|
package com.tangem.domain.staking.usecase
|
||||||
|
|
||||||
import com.tangem.domain.staking.model.stakekit.Yield
|
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.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
|
@ -12,10 +12,10 @@ import kotlinx.coroutines.flow.map
|
||||||
* - key: currency staking key (network.backendId + "_" + symbol)
|
* - key: currency staking key (network.backendId + "_" + symbol)
|
||||||
* - value: validators
|
* - value: validators
|
||||||
*/
|
*/
|
||||||
class StakingApyFlowUseCase(private val stakingRepository: StakingRepository) {
|
class StakingApyFlowUseCase(private val stakeKitRepository: StakeKitRepository) {
|
||||||
|
|
||||||
operator fun invoke(): Flow<Map<String, List<Yield.Validator>>> {
|
operator fun invoke(): Flow<Map<String, List<Yield.Validator>>> {
|
||||||
return stakingRepository.getEnabledYields()
|
return stakeKitRepository.getEnabledYields()
|
||||||
.map { yields ->
|
.map { yields ->
|
||||||
yields.associate { yield ->
|
yields.associate { yield ->
|
||||||
val key = "${yield.token.coinGeckoId}_${yield.token.symbol}"
|
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.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${ 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${ 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: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: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>
|
<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