Updated on 2026-08-14
This commit is contained in:
commit
928fde1fc8
1397 changed files with 55981 additions and 16540 deletions
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>MultilineLambdaItParameter:FetchStakingYieldBalanceUseCase.kt$FetchStakingYieldBalanceUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$it")) StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() } return@either }</ID>
|
||||
<ID>MultilineLambdaItParameter:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase${ !it.isPending && action.amount < it.amount && it.type == BalanceType.STAKED && it.validatorAddress == action.validatorAddress }</ID>
|
||||
<ID>NamedArguments:GetConstructedStakingTransactionUseCase.kt$GetConstructedStakingTransactionUseCase$constructTransaction(networkId, fee, amount, transactionId)</ID>
|
||||
<ID>UnnecessaryAbstractClass:MultiStakingBalanceSupplier.kt$MultiStakingBalanceSupplier$MultiStakingBalanceSupplier</ID>
|
||||
<ID>UnnecessaryAbstractClass:SingleStakingBalanceSupplier.kt$SingleStakingBalanceSupplier$SingleStakingBalanceSupplier</ID>
|
||||
<ID>UseEmptyCounterpart:StakingAnalyticsEvent.kt$StakingAnalyticsEvent$mapOf()</ID>
|
||||
<ID>UseOrEmpty:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase$action.validatorAddress ?: action.validatorAddresses?.getOrNull(0) ?: ""</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -23,9 +23,9 @@ class FetchStakingYieldBalanceUseCase(
|
|||
currencyId = cryptoCurrency.id,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
.getOrElse {
|
||||
when (it) {
|
||||
is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$it"))
|
||||
.getOrElse { error ->
|
||||
when (error) {
|
||||
is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$error"))
|
||||
StakingIdFactory.Error.UnsupportedCurrency -> Unit.right()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import arrow.core.Either
|
|||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
|
||||
class GetConstructedStakingTransactionUseCase(
|
||||
private val stakeKitRepository: StakeKitRepository,
|
||||
|
|
@ -15,12 +16,17 @@ class GetConstructedStakingTransactionUseCase(
|
|||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
networkId: String,
|
||||
networkId: Network.RawID,
|
||||
fee: Fee,
|
||||
amount: Amount,
|
||||
transactionId: String,
|
||||
): Either<StakingError, Pair<StakingTransaction, TransactionData.Compiled>> = Either.catch {
|
||||
stakeKitRepository.constructTransaction(networkId, fee, amount, transactionId)
|
||||
stakeKitRepository.constructTransaction(
|
||||
networkId = networkId,
|
||||
fee = fee,
|
||||
amount = amount,
|
||||
transactionId = transactionId,
|
||||
)
|
||||
}.mapLeft {
|
||||
stakingErrorResolver.resolve(it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class InvalidatePendingTransactionsUseCase(
|
|||
type = BalanceType.STAKED,
|
||||
amount = action.amount,
|
||||
rawCurrencyId = null,
|
||||
validatorAddress = action.validatorAddress ?: action.validatorAddresses?.getOrNull(0) ?: "",
|
||||
validatorAddress = action.validatorAddress ?: action.validatorAddresses?.getOrNull(0).orEmpty(),
|
||||
date = null,
|
||||
pendingActions = emptyList(),
|
||||
pendingActionsConstraints = emptyList(),
|
||||
|
|
@ -149,10 +149,10 @@ class InvalidatePendingTransactionsUseCase(
|
|||
}
|
||||
|
||||
private fun findPartialUnstake(balances: MutableList<BalanceItem>, action: StakingAction): Pair<Int, BigDecimal> {
|
||||
val index = balances.indexOfFirst {
|
||||
!it.isPending && action.amount < it.amount &&
|
||||
it.type == BalanceType.STAKED &&
|
||||
it.validatorAddress == action.validatorAddress
|
||||
val index = balances.indexOfFirst { balance ->
|
||||
!balance.isPending && action.amount < balance.amount &&
|
||||
balance.type == BalanceType.STAKED &&
|
||||
balance.validatorAddress == action.validatorAddress
|
||||
}
|
||||
return index to action.amount
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,23 +2,27 @@ package com.tangem.domain.staking
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensure
|
||||
import arrow.core.raise.ensureNotNull
|
||||
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.staking.StakingID
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
||||
/**
|
||||
* Factory class for creating instances of [StakingID]
|
||||
*
|
||||
* @property walletManagersFacade wallet manager facade
|
||||
* @property walletManagersFacade wallet manager facade
|
||||
* @property stakingFeatureToggles staking feature toggles
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class StakingIdFactory(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -72,6 +76,8 @@ class StakingIdFactory(
|
|||
|
||||
ensureNotNull(integrationId) { Error.UnsupportedCurrency }
|
||||
|
||||
ensure(stakingFeatureToggles.isIntegrationEnabled(integrationId)) { Error.UnsupportedCurrency }
|
||||
|
||||
val address = defaultAddressProvider().takeUnless { it.isNullOrEmpty() }
|
||||
|
||||
ensureNotNull(address) { Error.UnableToGetAddress(integrationId = integrationId) }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.domain.models.staking.action.StakingActionType
|
|||
|
||||
sealed class StakingAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(
|
||||
category = "Staking",
|
||||
event = event,
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ sealed interface StakingIntegrationID {
|
|||
* @return a [StakingIntegrationID] if supported, or `null` if not supported.
|
||||
*/
|
||||
fun create(currencyId: CryptoCurrency.ID): StakingIntegrationID? {
|
||||
val blockchain = Blockchain.fromId(id = currencyId.rawNetworkId)
|
||||
val blockchain = currencyId.toBlockchain()
|
||||
|
||||
return if (currencyId.contractAddress.isNullOrBlank()) {
|
||||
// Order is not important — either P2PEthPool or Stakekit.Coin can be in any order
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.domain.models.staking.StakingBalance
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class MultiStakingBalanceSupplier(
|
||||
open class MultiStakingBalanceSupplier(
|
||||
override val factory: FlowProducer.Factory<MultiStakingBalanceProducer.Params, MultiStakingBalanceProducer>,
|
||||
override val keyCreator: (MultiStakingBalanceProducer.Params) -> String,
|
||||
) : FlowCachingSupplier<MultiStakingBalanceProducer, MultiStakingBalanceProducer.Params, Set<StakingBalance>>()
|
||||
|
|
@ -5,11 +5,11 @@ 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.staking.NetworkType
|
||||
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.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
|
||||
|
|
@ -55,7 +55,7 @@ interface StakeKitRepository {
|
|||
suspend fun estimateGas(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingGasEstimate
|
||||
|
||||
suspend fun constructTransaction(
|
||||
networkId: String,
|
||||
networkId: Network.RawID,
|
||||
fee: Fee,
|
||||
amount: Amount,
|
||||
transactionId: String,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.domain.models.staking.StakingBalance
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class SingleStakingBalanceSupplier(
|
||||
open class SingleStakingBalanceSupplier(
|
||||
override val factory: FlowProducer.Factory<SingleStakingBalanceProducer.Params, SingleStakingBalanceProducer>,
|
||||
override val keyCreator: (SingleStakingBalanceProducer.Params) -> String,
|
||||
) : FlowCachingSupplier<SingleStakingBalanceProducer, SingleStakingBalanceProducer.Params, StakingBalance>()
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.domain.staking.toggles
|
||||
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
|
||||
interface StakingFeatureToggles {
|
||||
val isEthStakingEnabled: Boolean
|
||||
|
||||
fun isIntegrationEnabled(integrationId: StakingIntegrationID): Boolean
|
||||
}
|
||||
|
|
@ -5,17 +5,16 @@ import arrow.core.left
|
|||
import arrow.core.right
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.test.core.ProvideTestModels
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
|
|
@ -30,11 +29,16 @@ import org.junit.jupiter.params.ParameterizedTest
|
|||
internal class StakingIdFactoryTest {
|
||||
|
||||
private val walletManagersFacade: WalletManagersFacade = mockk()
|
||||
private val factory = StakingIdFactory(walletManagersFacade = walletManagersFacade)
|
||||
private val stakingFeatureToggles: StakingFeatureToggles = mockk()
|
||||
private val factory = StakingIdFactory(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(walletManagersFacade)
|
||||
clearMocks(walletManagersFacade, stakingFeatureToggles)
|
||||
every { stakingFeatureToggles.isIntegrationEnabled(any()) } returns true
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
@ -66,6 +70,33 @@ internal class StakingIdFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create returns UnsupportedCurrency if integration is disabled by toggle`() = runTest {
|
||||
// Arrange
|
||||
val userWalletId = UserWalletId(stringValue = "011")
|
||||
val currency = MockCryptoCurrencyFactory().createCoin(Blockchain.TON)
|
||||
|
||||
every {
|
||||
stakingFeatureToggles.isIntegrationEnabled(StakingIntegrationID.StakeKit.Coin.Ton)
|
||||
} returns false
|
||||
|
||||
// Act
|
||||
val actual = factory.create(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = StakingIdFactory.Error.UnsupportedCurrency
|
||||
|
||||
Truth.assertThat(actual.leftOrNull()).isEqualTo(expected)
|
||||
|
||||
coVerify(inverse = true) {
|
||||
walletManagersFacade.getDefaultAddress(userWalletId = any(), network = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create returns UnableToGetAddress if address is null`() = runTest {
|
||||
// Arrange
|
||||
|
|
@ -154,7 +185,7 @@ internal class StakingIdFactoryTest {
|
|||
),
|
||||
CreateModel(
|
||||
currencyId = CryptoCurrency.ID.fromValue(
|
||||
value = "token⟨ETH⟩polygon-ecosystem-token⚓0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
|
||||
value = "token⟨ethereum⟩polygon-ecosystem-token⚓0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
|
||||
),
|
||||
expected = createStakingId(integrationId = StakingIntegrationID.StakeKit.EthereumToken.Polygon),
|
||||
),
|
||||
|
|
@ -168,6 +199,6 @@ internal class StakingIdFactoryTest {
|
|||
data class CreateModel(val currencyId: CryptoCurrency.ID, val expected: Either<StakingIdFactory.Error, StakingID>)
|
||||
|
||||
private fun createCurrencyId(blockchain: Blockchain): CryptoCurrency.ID {
|
||||
return CryptoCurrency.ID.fromValue(value = "coin⟨${blockchain.id}⟩")
|
||||
return CryptoCurrency.ID.fromValue(value = "coin⟨${blockchain.toNetworkId()}⟩")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.staking
|
|||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
|
|
@ -144,11 +145,11 @@ class StakingIntegrationIDTest {
|
|||
expected = StakingIntegrationID.P2PEthPool,
|
||||
),
|
||||
CreateModel(
|
||||
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨ETH⟩polygon-ecosystem-token⚓1234567890"),
|
||||
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨ethereum⟩polygon-ecosystem-token⚓1234567890"),
|
||||
expected = StakingIntegrationID.StakeKit.EthereumToken.Polygon,
|
||||
),
|
||||
CreateModel(
|
||||
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨SOLANA⟩solana⚓1234567890"),
|
||||
currencyId = CryptoCurrency.ID.fromValue(value = "token⟨solana⟩solana⚓1234567890"),
|
||||
expected = null,
|
||||
),
|
||||
)
|
||||
|
|
@ -157,6 +158,6 @@ class StakingIntegrationIDTest {
|
|||
data class CreateModel(val currencyId: CryptoCurrency.ID, val expected: StakingIntegrationID?)
|
||||
|
||||
private fun createCurrencyId(blockchain: Blockchain): CryptoCurrency.ID {
|
||||
return CryptoCurrency.ID.fromValue(value = "coin⟨${blockchain.id}⟩")
|
||||
return CryptoCurrency.ID.fromValue(value = "coin⟨${blockchain.toNetworkId()}⟩")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue