Updated on 2026-08-14
This commit is contained in:
parent
a4340e40a8
commit
d7c4218f92
8 changed files with 74 additions and 37 deletions
|
|
@ -1,8 +1,19 @@
|
|||
package com.tangem.utils.transformer
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
/**
|
||||
* Transforms state to updated state.
|
||||
*/
|
||||
interface Transformer<S> {
|
||||
fun transform(prevState: S): S
|
||||
}
|
||||
|
||||
fun <T> MutableStateFlow<T>.update(transformer: Transformer<T>) {
|
||||
update { transformer.transform(this.value) }
|
||||
}
|
||||
|
||||
fun <T> MutableStateFlow<T>.updateAll(vararg transformers: Transformer<T>) {
|
||||
transformers.forEach { transformer -> update { transformer.transform(this.value) } }
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ dependencies {
|
|||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,18 @@
|
|||
package com.tangem.domain.transaction.error
|
||||
|
||||
sealed class ValidateAddressError {
|
||||
data object AddressInWallet : ValidateAddressError()
|
||||
data object InvalidAddress : ValidateAddressError()
|
||||
data class DataError(val throwable: Throwable) : ValidateAddressError()
|
||||
import arrow.core.Either
|
||||
|
||||
typealias AddressValidationResult = Either<AddressValidation.Error, AddressValidation.Success>
|
||||
|
||||
sealed class AddressValidation {
|
||||
sealed class Success : AddressValidation() {
|
||||
data object Valid : Success()
|
||||
data object ValidXAddress : Success()
|
||||
}
|
||||
|
||||
sealed class Error : AddressValidation() {
|
||||
data object AddressInWallet : Error()
|
||||
data object InvalidAddress : Error()
|
||||
data class DataError(val throwable: Throwable) : Error()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.domain.transaction.error
|
||||
|
||||
sealed class ValidateMemoError {
|
||||
data object InvalidMemo : ValidateMemoError()
|
||||
data class DataError(val throwable: Throwable) : ValidateMemoError()
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
package com.tangem.domain.transaction.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
import com.tangem.domain.transaction.error.ValidateAddressError
|
||||
import com.tangem.domain.transaction.error.AddressValidation
|
||||
import com.tangem.domain.transaction.error.AddressValidationResult
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
|
||||
/**
|
||||
* Use case for validating wallet address.
|
||||
|
|
@ -23,18 +24,23 @@ class ValidateWalletAddressUseCase(
|
|||
network: Network,
|
||||
address: String,
|
||||
currencyAddress: Set<NetworkAddress.Address>?,
|
||||
): Either<ValidateAddressError, Unit> {
|
||||
): AddressValidationResult {
|
||||
val isUtxoConsolidationAvailable =
|
||||
walletManagersFacade.checkUtxoConsolidationAvailability(userWalletId, network)
|
||||
val isCurrentAddress = currencyAddress?.any { it.value == address } ?: true
|
||||
|
||||
val isForbidSelfSend = isCurrentAddress && !isUtxoConsolidationAvailable
|
||||
val isValidAddress = walletAddressServiceRepository.validateAddress(userWalletId, network, address)
|
||||
|
||||
val decodedXAddress = BlockchainUtils.decodeRippleXAddress(address, network.id.value)
|
||||
val addressToValidate = decodedXAddress?.address ?: address
|
||||
|
||||
val isValidAddress = walletAddressServiceRepository.validateAddress(userWalletId, network, addressToValidate)
|
||||
|
||||
return when {
|
||||
!isValidAddress -> ValidateAddressError.InvalidAddress.left()
|
||||
isForbidSelfSend -> ValidateAddressError.AddressInWallet.left()
|
||||
else -> Unit.right()
|
||||
!isValidAddress -> AddressValidation.Error.InvalidAddress.left()
|
||||
isForbidSelfSend -> AddressValidation.Error.AddressInWallet.left()
|
||||
decodedXAddress != null -> AddressValidation.Success.ValidXAddress.right()
|
||||
else -> AddressValidation.Success.Valid.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.domain.transaction.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
import com.tangem.domain.transaction.error.ValidateMemoError
|
||||
|
||||
/**
|
||||
* Use case for validating wallet memo.
|
||||
|
|
@ -11,7 +14,16 @@ class ValidateWalletMemoUseCase(
|
|||
private val walletAddressServiceRepository: WalletAddressServiceRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(network: Network, memo: String): Either<Throwable, Boolean> = Either.catch {
|
||||
walletAddressServiceRepository.validateMemo(network, memo)
|
||||
operator fun invoke(network: Network, memo: String): Either<ValidateMemoError, Unit> {
|
||||
return try {
|
||||
val isValidMemo = walletAddressServiceRepository.validateMemo(network, memo)
|
||||
if (isValidMemo) {
|
||||
Unit.right()
|
||||
} else {
|
||||
ValidateMemoError.InvalidMemo.left()
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
ValidateMemoError.DataError(ex).left()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,8 +36,9 @@ import com.tangem.domain.tokens.*
|
|||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.AddressValidation
|
||||
import com.tangem.domain.transaction.error.AddressValidationResult
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.error.ValidateAddressError
|
||||
import com.tangem.domain.transaction.usecase.*
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase
|
||||
|
|
@ -116,7 +117,7 @@ internal class SendModel @Inject constructor(
|
|||
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
private val innerRouter: InnerSendRouter,
|
||||
private val appRouter: AppRouter,
|
||||
appRouter: AppRouter,
|
||||
paramsContainer: ParamsContainer,
|
||||
validateTransactionUseCase: ValidateTransactionUseCase,
|
||||
getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
|
||||
|
|
@ -693,7 +694,7 @@ internal class SendModel @Inject constructor(
|
|||
}.saveIn(memoValidationJobHolder)
|
||||
}
|
||||
|
||||
private suspend fun validateAddress(value: String): Either<ValidateAddressError, Unit> = runCatching {
|
||||
private suspend fun validateAddress(value: String): AddressValidationResult = runCatching {
|
||||
val maybeValidAddress = validateWalletAddressUseCase(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrency.network,
|
||||
|
|
@ -702,10 +703,10 @@ internal class SendModel @Inject constructor(
|
|||
)
|
||||
onEnteredValidAddress(maybeValidAddress.isLeft())
|
||||
maybeValidAddress
|
||||
}.getOrElse { ValidateAddressError.DataError(it).left() }
|
||||
}.getOrElse { AddressValidation.Error.DataError(it).left() }
|
||||
|
||||
private fun validateMemo(value: String?): Boolean {
|
||||
return value?.let { validateWalletMemoUseCase(cryptoCurrency.network, it).getOrElse { false } } ?: true
|
||||
return value?.let { validateWalletMemoUseCase(cryptoCurrency.network, it).isRight() } != false
|
||||
}
|
||||
|
||||
private suspend fun checkIfXrpAddressValue(value: String): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
package com.tangem.features.send.impl.presentation.state.recipient
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.transaction.error.ValidateAddressError
|
||||
import com.tangem.domain.transaction.error.AddressValidation
|
||||
import com.tangem.domain.transaction.error.AddressValidationResult
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
||||
internal class RecipientSendFactory(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
|
|
@ -66,10 +64,7 @@ internal class RecipientSendFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getOnRecipientAddressValidState(
|
||||
value: String,
|
||||
maybeValidAddress: Either<ValidateAddressError, Unit>,
|
||||
): SendUiState {
|
||||
fun getOnRecipientAddressValidState(value: String, maybeValidAddress: AddressValidationResult): SendUiState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val state = currentStateProvider()
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
|
|
@ -78,10 +73,7 @@ internal class RecipientSendFactory(
|
|||
val isValidMemo = validateWalletMemoUseCase(
|
||||
memo = recipientState.memoTextField?.value.orEmpty(),
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
).getOrElse {
|
||||
Timber.e("Failed to validateWalletMemoUseCase: $it")
|
||||
false
|
||||
}
|
||||
).isRight()
|
||||
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
|
|
@ -92,10 +84,10 @@ internal class RecipientSendFactory(
|
|||
error = maybeValidAddress.fold(
|
||||
ifLeft = {
|
||||
when (it) {
|
||||
ValidateAddressError.InvalidAddress -> resourceReference(
|
||||
AddressValidation.Error.InvalidAddress -> resourceReference(
|
||||
R.string.send_recipient_address_error,
|
||||
)
|
||||
ValidateAddressError.AddressInWallet -> resourceReference(
|
||||
AddressValidation.Error.AddressInWallet -> resourceReference(
|
||||
R.string.send_error_address_same_as_wallet,
|
||||
)
|
||||
else -> null
|
||||
|
|
@ -143,10 +135,7 @@ internal class RecipientSendFactory(
|
|||
val isValidMemo = validateWalletMemoUseCase(
|
||||
memo = value,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
).getOrElse {
|
||||
Timber.e("Failed to validateWalletMemoUseCase: $it")
|
||||
false
|
||||
}
|
||||
).isRight()
|
||||
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue