Updated on 2026-08-14
This commit is contained in:
parent
0b671b4ac1
commit
1d75e884c8
15 changed files with 340 additions and 23 deletions
|
|
@ -12,10 +12,17 @@ android {
|
|||
|
||||
dependencies {
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
/** Common modules */
|
||||
implementation(projects.data.common)
|
||||
|
||||
/** Domain modules */
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.staking)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
|
||||
// region DI
|
||||
|
|
|
|||
|
|
@ -2,23 +2,33 @@ package com.tangem.data.staking
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.staking.converters.StakingNetworkTypeConverter
|
||||
import com.tangem.data.staking.converters.TokenConverter
|
||||
import com.tangem.data.staking.converters.YieldConverter
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.converters.*
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.request.Address
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.domain.staking.model.*
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultStakingRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val stakingBalanceStore: StakingBalanceStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : StakingRepository {
|
||||
|
||||
|
|
@ -31,6 +41,10 @@ internal class DefaultStakingRepository(
|
|||
tokenConverter = tokenConverter,
|
||||
)
|
||||
|
||||
private val yieldBalanceConverter = YieldBalanceConverter()
|
||||
|
||||
private val yieldBalanceListConverter = YieldBalanceListConverter()
|
||||
|
||||
override fun isStakingSupported(currencyId: String): Boolean {
|
||||
return integrationIds.contains(currencyId)
|
||||
}
|
||||
|
|
@ -94,9 +108,93 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun fetchSingleYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
networkStatus: NetworkStatus,
|
||||
integrationId: String,
|
||||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val result = stakeKitApi.getSingleYieldBalance(
|
||||
integrationId = integrationId,
|
||||
body = getBalanceRequestData(networkStatus),
|
||||
).getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(
|
||||
integrationId,
|
||||
YieldBalanceWrapperDTO(
|
||||
balances = result,
|
||||
integrationId = integrationId,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSingleYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
networkStatus: NetworkStatus,
|
||||
integrationId: String,
|
||||
): Flow<List<YieldBalance>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get(integrationId)
|
||||
.collectLatest {
|
||||
send(yieldBalanceConverter.convertList(it))
|
||||
}
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchSingleYieldBalance(
|
||||
userWalletId,
|
||||
networkStatus,
|
||||
integrationId,
|
||||
)
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
override suspend fun fetchMultiYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<NetworkStatus>,
|
||||
integrationId: String,
|
||||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val result = stakeKitApi.getMultipleYieldBalances(
|
||||
networks.map(::getBalanceRequestData),
|
||||
).getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(result)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<NetworkStatus>,
|
||||
integrationId: String,
|
||||
): Flow<List<YieldBalanceList>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get()
|
||||
.collectLatest { send(yieldBalanceListConverter.convertList(it)) }
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchMultiYieldBalance(
|
||||
userWalletId,
|
||||
networks,
|
||||
integrationId,
|
||||
)
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: String, symbol: String): Yield? {
|
||||
return yields
|
||||
.find { it.token.coinGeckoId == currencyId && it.token.symbol == symbol }
|
||||
return yields.find { it.token.coinGeckoId == currencyId && it.token.symbol == symbol }
|
||||
}
|
||||
|
||||
private suspend fun getEnabledYields(): List<Yield>? {
|
||||
|
|
@ -104,6 +202,28 @@ internal class DefaultStakingRepository(
|
|||
return yields.map { yieldConverter.convert(it) }
|
||||
}
|
||||
|
||||
private fun getBalanceRequestData(networkStatus: NetworkStatus): YieldBalanceRequestBody {
|
||||
val networkAddress = when (val network = networkStatus.value) {
|
||||
NetworkStatus.MissedDerivation -> null
|
||||
is NetworkStatus.NoAccount -> network.address
|
||||
is NetworkStatus.Unreachable -> network.address
|
||||
is NetworkStatus.Verified -> network.address
|
||||
}
|
||||
val address = networkAddress?.defaultAddress?.value.orEmpty()
|
||||
return YieldBalanceRequestBody(
|
||||
addresses = Address(
|
||||
address = address,
|
||||
additionalAddresses = null, // todo fill additional addresses metadata if needed
|
||||
explorerUrl = "", // todo fill exporer url
|
||||
),
|
||||
args = YieldBalanceRequestBody.YieldBalanceRequestArgs(
|
||||
validatorAddresses = listOf(), // todo add validator addresses
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getYieldBalancesKey(userWalletId: UserWalletId) = "yield_balance_${userWalletId.stringValue}"
|
||||
|
||||
companion object {
|
||||
private val integrationIds = setOf(
|
||||
Blockchain.Solana.toCoinId(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.data.staking.converters
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
|
||||
import com.tangem.domain.staking.model.BalanceType
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class YieldBalanceConverter : Converter<BalanceDTO, YieldBalance> {
|
||||
override fun convert(value: BalanceDTO): YieldBalance {
|
||||
return YieldBalance(
|
||||
type = BalanceType.valueOf(value.type.name),
|
||||
amount = value.amount,
|
||||
pricePerShare = value.pricePerShare,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.data.staking.converters
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.domain.staking.model.YieldBalanceList
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class YieldBalanceListConverter : Converter<YieldBalanceWrapperDTO, YieldBalanceList> {
|
||||
|
||||
internal val converter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
YieldBalanceConverter()
|
||||
}
|
||||
|
||||
override fun convert(value: YieldBalanceWrapperDTO): YieldBalanceList {
|
||||
return YieldBalanceList(
|
||||
balances = converter.convertList(value.balances),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.data.staking.di
|
||||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.DefaultStakingRepository
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -20,12 +22,16 @@ internal object StakingDataModule {
|
|||
fun provideStakingRepository(
|
||||
stakeKitApi: StakeKitApi,
|
||||
stakingTokenStore: StakingYieldsStore,
|
||||
stakingBalanceStore: StakingBalanceStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
cacheRegistry: CacheRegistry,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
stakeKitApi = stakeKitApi,
|
||||
stakingYieldsStore = stakingTokenStore,
|
||||
stakingBalanceStore = stakingBalanceStore,
|
||||
dispatchers = dispatchers,
|
||||
cacheRegistry = cacheRegistry,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue