Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-27 20:18:41 +03:00
parent c04036723e
commit 4dc5d045a2
12 changed files with 225 additions and 106 deletions

View file

@ -14,7 +14,6 @@ import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
@ -77,13 +76,11 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState, modifier: Modifier
val isSending = uiState.sendState?.isSending?.collectAsStateWithLifecycle()?.value ?: false
val txUrl = uiState.sendState?.txUrl?.collectAsStateWithLifecycle()?.value.orEmpty()
val (buttonTextId, buttonClick) = remember {
getButtonData(
currentState = currentState,
isSuccess = isSuccess,
uiState = uiState,
)
}
val (buttonTextId, buttonClick) = getButtonData(
currentState = currentState,
isSuccess = isSuccess,
uiState = uiState,
)
val isButtonEnabled = isButtonEnabled(
currentState = currentState,

View file

@ -1,52 +0,0 @@
package com.tangem.features.send.impl.presentation.viewmodel
import androidx.core.text.isDigitsOnly
import com.tangem.blockchain.common.Blockchain
import com.tangem.domain.tokens.model.CryptoCurrency
import java.math.BigInteger
internal fun validateMemo(memo: String, cryptoCurrency: CryptoCurrency?): Boolean {
if (cryptoCurrency == null) return false
if (memo.isEmpty()) return true
return when (cryptoCurrency.network.id.value) {
Blockchain.XRP.id -> {
val tag = memo.toLongOrNull()
tag != null && tag <= XRP_TAG_MAX_NUMBER
}
Blockchain.Stellar.id -> {
isAssignableValue(memo)
}
else -> true
}
}
private fun isAssignableValue(value: String): Boolean {
val memoType = when {
value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID
else -> XlmMemoType.TEXT
}
return when (memoType) {
XlmMemoType.TEXT -> {
// from org.stellar.sdk.MemoText
value.toByteArray().size <= XLM_MEMO_MAX_LENGTH
}
XlmMemoType.ID -> {
try {
// from com.tangem.blockchain.blockchains.stellar.StellarMemo.toStellarSdkMemo
value.toBigInteger() in BigInteger.ZERO..Long.MAX_VALUE.toBigInteger() * 2.toBigInteger()
} catch (ex: NumberFormatException) {
false
}
}
}
}
fun determineXlmMemoType(value: String): XlmMemoType = when {
value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID
else -> XlmMemoType.TEXT
}
enum class XlmMemoType { TEXT, ID }
private const val XRP_TAG_MAX_NUMBER = 4294967295
private const val XLM_MEMO_MAX_LENGTH = 28

View file

@ -6,15 +6,8 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.*
import androidx.paging.PagingData
import arrow.core.getOrElse
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpAddressService
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionExtras
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -25,6 +18,7 @@ import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.utils.convertToAmount
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.models.TxHistoryItem
@ -57,6 +51,7 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
import kotlin.properties.Delegates
@ -75,6 +70,7 @@ internal class SendViewModel @Inject constructor(
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createTransactionUseCase: CreateTransactionUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val validateWalletAddressUseCase: ValidateWalletAddressUseCase,
private val walletManagersFacade: WalletManagersFacade,
@ -135,7 +131,7 @@ internal class SendViewModel @Inject constructor(
getCurrenciesStatusUpdates(owner, wallet)
},
ifLeft = {
// TODO add error handling
// todo add error handling [[REDACTED_JIRA]]
return@launch
},
)
@ -299,7 +295,7 @@ internal class SendViewModel @Inject constructor(
uiState = stateFactory.onFeeOnLoadedState(it, true)
},
ifLeft = {
// TODO add error handling
// todo add error handling [[REDACTED_JIRA]]
},
)
}
@ -421,7 +417,7 @@ internal class SendViewModel @Inject constructor(
val amountToSend = amount.toBigDecimal().convertToAmount(cryptoCurrency)
// todo add notifications [[REDACTED_JIRA]]
// todo add error handling [[REDACTED_JIRA]]
// val transactionErrors = walletManagersFacade.validateTransaction(
// amount = amountToSend,
// fee = fee.amount,
@ -429,32 +425,38 @@ internal class SendViewModel @Inject constructor(
// network = cryptoCurrency.network,
// )
val txData = walletManagersFacade.createTransaction(
createTransactionUseCase(
amount = amountToSend,
fee = fee,
memo = memo,
destination = recipient,
userWalletId = userWalletId,
network = cryptoCurrency.network,
)?.copy(extras = getMemoExtras(cryptoCurrency.network.id.value, memo)) ?: return
sendTransactionUseCase(
txData = txData,
userWallet = userWallet,
network = cryptoCurrency.network,
).fold(
ifLeft = {
sendState.isSending.update { false }
// todo add notifications [[REDACTED_JIRA]]
Timber.e(it)
// todo add error handling [[REDACTED_JIRA]]
},
ifRight = {
sendState.transactionDate.update {
txData.date?.timeInMillis ?: System.currentTimeMillis()
}
sendState.isSuccess.update { true }
sendState.txUrl.update {
getTxUrl(txData.hash.orEmpty())
}
ifRight = { txData ->
sendTransactionUseCase(
txData = txData,
userWallet = userWallet,
network = cryptoCurrency.network,
).fold(
ifLeft = {
sendState.isSending.update { false }
// todo add error handling [[REDACTED_JIRA]]
},
ifRight = {
sendState.transactionDate.update {
txData.date?.timeInMillis ?: System.currentTimeMillis()
}
sendState.isSuccess.update { true }
sendState.txUrl.update {
getTxUrl(txData.hash.orEmpty())
}
},
)
},
)
}
@ -499,25 +501,6 @@ internal class SendViewModel @Inject constructor(
}
// endregion
private fun getMemoExtras(networkId: String, memo: String?): TransactionExtras? {
val blockchain = Blockchain.fromId(networkId)
if (memo == null) return null
return when (blockchain) {
Blockchain.Stellar -> {
val xmlMemo = when (determineXlmMemoType(memo)) {
XlmMemoType.TEXT -> StellarMemo.Text(memo)
XlmMemoType.ID -> StellarMemo.Id(memo.toBigInteger())
}
StellarTransactionExtras(xmlMemo)
}
Blockchain.Binance -> BinanceTransactionExtras(memo)
Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) }
Blockchain.Cosmos -> CosmosTransactionExtras(memo)
Blockchain.TON -> TonTransactionExtras(memo)
else -> null
}
}
companion object {
private const val XRP_X_ADDRESS = 'X'
private const val DEFAULT_VALUE = "0.00"