Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-01 15:24:59 +07:00
parent ab2f0f58bb
commit 22ad2ddc67
22 changed files with 190 additions and 15 deletions

View file

@ -8,7 +8,11 @@ internal class SdkRequirementsConditionConverter : Converter<SdkRequirementsCond
override fun convert(value: SdkRequirementsCondition): AssetRequirementsCondition {
return when (value) {
is SdkRequirementsCondition.PaidTransaction -> AssetRequirementsCondition.PaidTransaction
is SdkRequirementsCondition.RequiredTrustline -> TODO("added in [REDACTED_TASK_KEY]")
is SdkRequirementsCondition.RequiredTrustline -> AssetRequirementsCondition.RequiredTrustline(
requiredAmount = requireNotNull(value.amount.value),
currencySymbol = value.amount.currencySymbol,
decimals = value.amount.decimals,
)
is SdkRequirementsCondition.PaidTransactionWithFee -> AssetRequirementsCondition.PaidTransactionWithFee(
feeAmount = requireNotNull(value.feeAmount.value),
feeCurrencySymbol = value.feeAmount.currencySymbol,

View file

@ -54,6 +54,8 @@ sealed class ScenarioUnavailabilityReason {
data object UnassociatedAsset : ScenarioUnavailabilityReason()
data object TrustlineRequired : ScenarioUnavailabilityReason()
enum class WithdrawalScenario {
SELL, SEND // TODO staking create&process STAKING
}

View file

@ -167,6 +167,7 @@ sealed class TokenScreenAnalyticsEvent(
private const val SINGLE_WALLET = "Single Wallet"
private const val LOADING = "Loading"
private const val ASSET_REQUIREMENT = "AssetRequirement"
private const val TRUSTLINE_REQUIREMENT = "TrustlineRequirement"
@Suppress("CyclomaticComplexMethod")
fun ScenarioUnavailabilityReason.toReasonAnalyticsText(): String {
@ -177,6 +178,7 @@ sealed class TokenScreenAnalyticsEvent(
is ScenarioUnavailabilityReason.StakingUnavailable,
-> UNAVAILABLE
ScenarioUnavailabilityReason.UnassociatedAsset -> ASSET_REQUIREMENT
ScenarioUnavailabilityReason.TrustlineRequired -> TRUSTLINE_REQUIREMENT
is ScenarioUnavailabilityReason.EmptyBalance -> EMPTY
is ScenarioUnavailabilityReason.PendingTransaction -> PENDING
ScenarioUnavailabilityReason.UsedOutdatedData -> CACHING

View file

@ -61,4 +61,11 @@ sealed class CryptoCurrencyWarning {
) : CryptoCurrencyWarning()
data object UsedOutdatedDataWarning : CryptoCurrencyWarning()
data class RequiredTrustline(
val currency: CryptoCurrency,
val currencySymbol: String,
val requiredAmount: BigDecimal,
val currencyDecimals: Int,
) : CryptoCurrencyWarning()
}

View file

@ -375,12 +375,14 @@ class GetCryptoCurrencyActionsUseCase(
}
private fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason {
return if (requirements is AssetRequirementsCondition.PaidTransaction ||
requirements is AssetRequirementsCondition.PaidTransactionWithFee
) {
ScenarioUnavailabilityReason.UnassociatedAsset
} else {
ScenarioUnavailabilityReason.None
return when (requirements) {
AssetRequirementsCondition.PaidTransaction,
is AssetRequirementsCondition.PaidTransactionWithFee,
-> ScenarioUnavailabilityReason.UnassociatedAsset
is AssetRequirementsCondition.IncompleteTransaction,
null,
-> ScenarioUnavailabilityReason.None
is AssetRequirementsCondition.RequiredTrustline -> ScenarioUnavailabilityReason.TrustlineRequired
}
}

View file

@ -222,6 +222,12 @@ class GetCurrencyWarningsUseCase(
): CryptoCurrencyWarning? {
return when (val requirements = walletManagersFacade.getAssetRequirements(userWalletId, currency)) {
is AssetRequirementsCondition.PaidTransaction -> HederaWarnings.AssociateWarning(currency = currency)
is AssetRequirementsCondition.RequiredTrustline -> CryptoCurrencyWarning.RequiredTrustline(
currency = currency,
requiredAmount = requirements.requiredAmount,
currencyDecimals = requirements.decimals,
currencySymbol = requirements.currencySymbol,
)
is AssetRequirementsCondition.PaidTransactionWithFee -> {
HederaWarnings.AssociateWarningWithFee(
currency = currency,

View file

@ -9,6 +9,15 @@ sealed class AssetRequirementsCondition {
*/
data object PaidTransaction : AssetRequirementsCondition()
/**
* Trustlines are an explicit opt-in for an account to hold a particular asset.
*/
data class RequiredTrustline(
val requiredAmount: BigDecimal,
val currencySymbol: String,
val decimals: Int,
) : AssetRequirementsCondition()
/**
* The exact value of the fee for this type of condition is stored in `feeAmount`.
*/

View file

@ -0,0 +1,10 @@
package com.tangem.domain.transaction.error
import java.math.BigDecimal
sealed interface OpenTrustlineError {
val message: String?
data class SomeError(override val message: String?) : OpenTrustlineError
data class NotEnoughCoin(val amount: BigDecimal, override val message: String?) : OpenTrustlineError
}

View file

@ -0,0 +1,44 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.transaction.error.OpenTrustlineError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
class OpenTrustlineUseCase(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Either<OpenTrustlineError, Unit> {
return either {
val signer = cardSdkConfigRepository.getCommonSigner(
cardId = null,
twinKey = null, // use null here because no assets support for Twin cards
)
catch(
block = {
when (val result = walletManagersFacade.fulfillRequirements(userWalletId, currency, signer)) {
is SimpleResult.Failure -> when (val error = result.error) {
is BlockchainSdkError.Stellar.MinReserveRequired ->
raise(OpenTrustlineError.NotEnoughCoin(error.amount, result.error.customMessage))
else -> raise(OpenTrustlineError.SomeError(result.error.customMessage))
}
SimpleResult.Success -> Unit
}
},
catch = { error -> raise(OpenTrustlineError.SomeError(error.message)) },
)
}
}
}