Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-03 15:30:31 +02:00
parent 193dea813e
commit ca286232ba
13 changed files with 111 additions and 92 deletions

View file

@ -219,7 +219,7 @@ internal class ChildFactory @Inject constructor(
params = StakingComponent.Params(
userWalletId = route.userWalletId,
cryptoCurrencyId = route.cryptoCurrencyId,
yield = route.yield,
yieldId = route.yieldId,
),
componentFactory = stakingComponentFactory,
)
@ -376,7 +376,7 @@ internal class ChildFactory @Inject constructor(
params = StakingComponent.Params(
userWalletId = route.userWalletId,
cryptoCurrencyId = route.cryptoCurrencyId,
yield = route.yield,
yieldId = route.yieldId,
),
componentFactory = stakingComponentFactory,
)

View file

@ -10,7 +10,6 @@ import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.serialization.Serializable
@ -257,8 +256,8 @@ sealed class AppRoute(val path: String) : Route {
data class Staking(
val userWalletId: UserWalletId,
val cryptoCurrencyId: CryptoCurrency.ID,
val yield: Yield,
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrencyId.value}/${yield.id}")
val yieldId: String,
) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrencyId.value}/$yieldId")
@Serializable
data object PushNotification : AppRoute(path = "/push_notification")

View file

@ -133,6 +133,12 @@ internal class DefaultStakingRepository(
}
}
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,

View file

@ -20,4 +20,10 @@ class GetYieldUseCase(
.catch { stakingRepository.getYield(cryptoCurrencyId, symbol) }
.mapLeft { stakingErrorResolver.resolve(it) }
}
suspend operator fun invoke(yieldId: String): Either<StakingError, Yield> {
return Either
.catch { stakingRepository.getYield(yieldId) }
.mapLeft { stakingErrorResolver.resolve(it) }
}
}

View file

@ -33,6 +33,8 @@ interface StakingRepository {
suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield
suspend fun getYield(yieldId: String): Yield
suspend fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability
suspend fun getActions(

View file

@ -39,78 +39,9 @@ class MockStakingRepository : StakingRepository {
rewardSchedule = Yield.Metadata.RewardSchedule.DAY,
)
override suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield = Yield(
id = "1",
token = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = "solana",
logoURI = null,
isPoints = null,
),
tokens = listOf(),
args = Yield.Args(
enter = Yield.Args.Enter(
addresses = Yield.Args.Enter.Addresses(
address = AddressArgument(
required = false,
network = null,
minimum = null,
maximum = null,
),
additionalAddresses = mapOf(),
),
args = mapOf(),
),
exit = null,
),
status = Yield.Status(enter = false, exit = null),
apy = 1.toBigDecimal(),
rewardRate = 2.3,
rewardType = Yield.RewardType.APR,
metadata = Yield.Metadata(
name = "Yield",
logoUri = "",
description = "",
documentation = null,
gasFeeToken = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = null,
logoURI = null,
isPoints = null,
),
token = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = null,
logoURI = null,
isPoints = null,
),
tokens = listOf(),
type = "auto",
rewardSchedule = Yield.Metadata.RewardSchedule.DAY,
cooldownPeriod = Yield.Metadata.Period(days = 1),
warmupPeriod = Yield.Metadata.Period(days = 1),
rewardClaiming = Yield.Metadata.RewardClaiming.AUTO,
defaultValidator = null,
minimumStake = null,
supportsMultipleValidators = false,
revshare = Yield.Metadata.Enabled(enabled = false),
fee = Yield.Metadata.Enabled(enabled = false),
),
validators = listOf(),
isAvailable = false,
)
override suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield = yield
override suspend fun getYield(yieldId: String) = yield
override suspend fun getStakingAvailability(
userWalletId: UserWalletId,
@ -245,4 +176,79 @@ class MockStakingRepository : StakingRepository {
override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval = StakingApproval.Empty
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean = false
private companion object {
val yield = Yield(
id = "1",
token = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = "solana",
logoURI = null,
isPoints = null,
),
tokens = listOf(),
args = Yield.Args(
enter = Yield.Args.Enter(
addresses = Yield.Args.Enter.Addresses(
address = AddressArgument(
required = false,
network = null,
minimum = null,
maximum = null,
),
additionalAddresses = mapOf(),
),
args = mapOf(),
),
exit = null,
),
status = Yield.Status(enter = false, exit = null),
apy = 1.toBigDecimal(),
rewardRate = 2.3,
rewardType = Yield.RewardType.APR,
metadata = Yield.Metadata(
name = "Yield",
logoUri = "",
description = "",
documentation = null,
gasFeeToken = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = null,
logoURI = null,
isPoints = null,
),
token = Token(
name = "Solana",
network = NetworkType.SOLANA,
symbol = "SOL",
decimals = 18,
address = null,
coinGeckoId = null,
logoURI = null,
isPoints = null,
),
tokens = listOf(),
type = "auto",
rewardSchedule = Yield.Metadata.RewardSchedule.DAY,
cooldownPeriod = Yield.Metadata.Period(days = 1),
warmupPeriod = Yield.Metadata.Period(days = 1),
rewardClaiming = Yield.Metadata.RewardClaiming.AUTO,
defaultValidator = null,
minimumStake = null,
supportsMultipleValidators = false,
revshare = Yield.Metadata.Enabled(enabled = false),
fee = Yield.Metadata.Enabled(enabled = false),
),
validators = listOf(),
isAvailable = false,
)
}
}

View file

@ -179,7 +179,7 @@ internal class TokenActionsHandler @AssistedInject constructor(
AppRoute.Staking(
userWalletId = cryptoCurrencyData.userWallet.walletId,
cryptoCurrencyId = cryptoCurrencyData.status.currency.id,
yield = yield,
yieldId = yield.id,
),
)
}

View file

@ -2,7 +2,6 @@ package com.tangem.features.staking.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
@ -11,7 +10,7 @@ interface StakingComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
val cryptoCurrencyId: CryptoCurrency.ID,
val yield: Yield,
val yieldId: String,
)
interface Factory : ComponentFactory<Params, StakingComponent>

View file

@ -26,10 +26,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.staking.GetActionsUseCase
import com.tangem.domain.staking.InvalidatePendingTransactionsUseCase
import com.tangem.domain.staking.IsAnyTokenStakedUseCase
import com.tangem.domain.staking.IsApproveNeededUseCase
import com.tangem.domain.staking.*
import com.tangem.domain.staking.analytics.StakeScreenSource
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
import com.tangem.domain.staking.model.StakingApproval
@ -81,6 +78,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.math.BigDecimal
import java.util.concurrent.CopyOnWriteArrayList
@ -119,6 +117,7 @@ internal class StakingModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getActionsUseCase: GetActionsUseCase,
private val getYieldUseCase: GetYieldUseCase,
private val paramsInterceptorHolder: ParamsInterceptorHolder,
private val shareManager: ShareManager,
@DelayedWork private val coroutineScope: CoroutineScope,
@ -139,7 +138,11 @@ internal class StakingModel @Inject constructor(
private val cryptoCurrencyId: CryptoCurrency.ID = params.cryptoCurrencyId
private val userWalletId: UserWalletId = params.userWalletId
private val yield: Yield = params.yield
private val yield: Yield = runBlocking {
getYieldUseCase(params.yieldId).getOrElse {
error("yield must be not null")
}
}
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
private var processingActions: List<StakingAction> = emptyList()

View file

@ -5,7 +5,6 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment
@ -39,12 +38,12 @@ internal class DefaultTokenDetailsRouter(
)
}
override fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yield: Yield) {
override fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yieldId: String) {
router.push(
AppRoute.Staking(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
yield = yield,
yieldId = yieldId,
),
)
}

View file

@ -1,6 +1,5 @@
package com.tangem.feature.tokendetails.presentation.router
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
@ -18,7 +17,7 @@ internal interface InnerTokenDetailsRouter : TokenDetailsRouter {
fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency)
fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yield: Yield)
fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yieldId: String)
fun openOnrampSuccess(externalTxId: String)
}

View file

@ -994,7 +994,7 @@ internal class TokenDetailsViewModel @Inject constructor(
error("Staking is unavailable for ${cryptoCurrency.name}")
}
router.openStaking(userWalletId, cryptoCurrency, yield)
router.openStaking(userWalletId, cryptoCurrency, yield.id)
}
}

View file

@ -409,7 +409,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
AppRoute.Staking(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
yield = yield ?: return@launch,
yieldId = yield?.id ?: return@launch,
),
)
}