Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-19 14:05:49 +03:00
parent 21961aaa74
commit 84022714e7
3 changed files with 133 additions and 41 deletions

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
import com.tangem.tap.common.redux.AppState
@ -9,6 +10,8 @@ import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetAddressError
import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetWalletAddress
import com.tangem.tap.features.send.redux.AddressVerifyAction.Error
import com.tangem.tap.scope
import kotlinx.coroutines.*
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
@ -17,6 +20,8 @@ import org.rekotlin.DispatchFunction
*/
internal class AddressMiddleware {
val addressValidator = AddressValidator()
fun handle(action: AddressActionUi, appState: AppState?, dispatch: (Action) -> Unit) {
when (action) {
is AddressActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch)
@ -54,13 +59,13 @@ internal class AddressMiddleware {
dispatch: (Action) -> Unit,
) {
val sendState = appState?.sendState ?: return
val wallet = sendState.walletManager?.wallet ?: return
val walletManager = sendState.walletManager ?: return
val address = sendState.addressState.normalFieldValue ?: return
val isUserInput = sendState.addressState.viewFieldValue.isFromUserInput
verifyAddress(
address = address,
wallet = wallet,
walletManager = walletManager,
isUserInput = isUserInput,
dispatch = dispatch,
sourceType = sourceType,
@ -68,6 +73,38 @@ internal class AddressMiddleware {
}
private fun verifyAddress(
address: String,
walletManager: WalletManager,
sourceType: AddressEntered.SourceType?,
isUserInput: Boolean,
dispatch: (Action) -> Unit,
) {
val wallet = walletManager.wallet
scope.launch(Dispatchers.Main) {
val failReason = withContext(Dispatchers.IO) {
addressValidator.validateAddress(walletManager, address)
}
if (failReason == null) {
dispatchSuccessValidationActions(
address = address,
wallet = wallet,
sourceType = sourceType,
isUserInput = isUserInput,
dispatch = dispatch,
)
} else {
dispatchFailedValidationActions(
failReason = failReason,
sourceType = sourceType,
dispatch = dispatch,
)
}
}
}
private fun dispatchSuccessValidationActions(
address: String,
wallet: Wallet,
sourceType: AddressEntered.SourceType?,
@ -113,57 +150,49 @@ internal class AddressMiddleware {
val supposedAddress = noSchemeAddress.removeShareUriQuery() // TODO: parse query?
val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress)
if (failReason == null) {
noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let {
dispatch(AmountAction.SetAmount(it, false))
dispatch(AmountActionUi.CheckAmountToSend)
}
dispatch(SetWalletAddress(supposedAddress, isUserInput))
dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null))
dispatch(FeeAction.RequestFee)
sourceType?.let {
Analytics.send(
event = AddressEntered(
sourceType = sourceType,
validationResult = AddressEntered.ValidationResult.Success,
),
)
}
} else {
dispatch(SetAddressError(failReason))
dispatch(TransactionExtrasAction.Release)
sourceType?.let {
Analytics.send(
event = AddressEntered(
sourceType = sourceType,
validationResult = AddressEntered.ValidationResult.Fail,
),
)
}
noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let {
dispatch(AmountAction.SetAmount(it, false))
dispatch(AmountActionUi.CheckAmountToSend)
}
dispatch(SetWalletAddress(supposedAddress, isUserInput))
dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null))
dispatch(FeeAction.RequestFee)
sourceType?.let {
Analytics.send(
event = AddressEntered(
sourceType = sourceType,
validationResult = AddressEntered.ValidationResult.Success,
),
)
}
}
private fun isValidBlockchainAddressAndNotTheSameAsWallet(wallet: Wallet, address: String): Error? {
return if (wallet.blockchain.validateAddress(address)) {
if (wallet.addresses.all { it.value != address }) {
null
} else {
Error.ADDRESS_SAME_AS_WALLET
}
} else {
Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN
private fun dispatchFailedValidationActions(
failReason: AddressVerifyAction.Error,
sourceType: AddressEntered.SourceType?,
dispatch: (Action) -> Unit,
) {
dispatch(SetAddressError(failReason))
dispatch(TransactionExtrasAction.Release)
sourceType?.let {
Analytics.send(
event = AddressEntered(
sourceType = sourceType,
validationResult = AddressEntered.ValidationResult.Fail,
),
)
}
}
private fun String.removeShareUriQuery(): String = this.substringBefore("?")
private fun String.getQueryParameter(name: String): String? {
return this.substringAfter("?").splitToMap("&", "=")[name]
}
private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) {
val address = input ?: return
val wallet = appState?.sendState?.walletManager?.wallet ?: return
val walletManager = appState?.sendState?.walletManager ?: return
val internalDispatcher: (Action) -> Unit = {
when (it) {
@ -178,7 +207,7 @@ internal class AddressMiddleware {
verifyAddress(
address = address,
wallet = wallet,
walletManager = walletManager,
sourceType = null,
isUserInput = false,
dispatch = internalDispatcher,

View file

@ -0,0 +1,52 @@
package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.blockchains.near.NearWalletManager
import com.tangem.blockchain.blockchains.near.network.NearAccount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.extensions.Result
import com.tangem.tap.features.send.redux.AddressVerifyAction
class AddressValidator {
suspend fun validateAddress(walletManager: WalletManager, address: String): AddressVerifyAction.Error? {
val blockchain = walletManager.wallet.blockchain
val wallet = walletManager.wallet
return if ((blockchain == Blockchain.Near || blockchain == Blockchain.NearTestnet) &&
address.length != NEAR_IMPLICIT_ADDRESS_LENGTH
) {
validateNearAddress(walletManager, address)
} else {
validateAddress(wallet, address)
}
}
private suspend fun validateNearAddress(
walletManager: WalletManager,
address: String,
): AddressVerifyAction.Error? {
val result = (walletManager as? NearWalletManager)?.getAccount(address)
return if (result is Result.Success && result.data is NearAccount.Full) {
null
} else {
AddressVerifyAction.Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN
}
}
private fun validateAddress(wallet: Wallet, address: String): AddressVerifyAction.Error? {
return if (wallet.blockchain.validateAddress(address)) {
if (wallet.addresses.all { it.value != address }) {
null
} else {
AddressVerifyAction.Error.ADDRESS_SAME_AS_WALLET
}
} else {
AddressVerifyAction.Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN
}
}
companion object {
const val NEAR_IMPLICIT_ADDRESS_LENGTH = 64
}
}