Updated on 2026-08-14

This commit is contained in:
Tangem 2022-09-19 09:56:40 +04:00
commit 886f0d66bc
54 changed files with 378 additions and 532 deletions

View file

@ -98,14 +98,16 @@ class DetailsMiddleware {
fun handle(action: DetailsAction.ResetToFactory) {
when (action) {
is DetailsAction.ResetToFactory.Start -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
}
is DetailsAction.ResetToFactory.Proceed -> {
val card = store.state.detailsState.cardSettingsState?.card ?: return
if (card.isTangemTwins()) {
store.dispatch(DetailsAction.ReCreateTwinsWallet)
return
} else {
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
}
}
is DetailsAction.ResetToFactory.Proceed -> {
val card = store.state.detailsState.cardSettingsState?.card ?: return
scope.launch {
val result = tangemSdkManager.resetToFactorySettings(card)
withContext(Dispatchers.Main) {

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.wallet.R
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
@ -11,11 +12,9 @@ import com.trustwallet.walletconnect.models.session.WCSession
import org.rekotlin.Action
sealed class WalletConnectAction : Action {
object ResetState : WalletConnectAction()
data class HandleDeepLink(val wcUri: String?) : WalletConnectAction()
object RestoreSessions : WalletConnectAction()
data class RestoreSessions(val scanResponse: ScanResponse) : WalletConnectAction()
data class StartWalletConnect(
val copiedUri: String?,
) : WalletConnectAction()

View file

@ -22,14 +22,13 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
import timber.log.Timber
class WalletConnectMiddleware {
private val walletConnectManager = WalletConnectManager()
private var walletConnectManager = WalletConnectManager()
val walletConnectMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
@ -43,8 +42,9 @@ class WalletConnectMiddleware {
if (DemoHelper.tryHandle(state, action)) return
when (action) {
is WalletConnectAction.ResetState -> walletConnectManager = WalletConnectManager()
is WalletConnectAction.RestoreSessions -> {
walletConnectManager.restoreSessions()
walletConnectManager.restoreSessions(action.scanResponse)
}
is WalletConnectAction.HandleDeepLink -> {
if (!action.wcUri.isNullOrBlank()) {
@ -74,7 +74,13 @@ class WalletConnectMiddleware {
is WalletConnectAction.ChooseNetwork -> {
val data = state()?.walletConnectState?.newSessionData ?: return
scope.launch {
prepareWalletManager(data.scanResponse, store.state.walletState, action.blockchain, data.session)
prepareWalletManager(
scanResponse = data.scanResponse,
walletState = store.state.walletState,
blockchain = action.blockchain,
session = data.session,
walletConnectManager = walletConnectManager,
)
}
}
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
@ -110,7 +116,8 @@ class WalletConnectMiddleware {
)
}
is WalletConnectAction.ScanCard -> {
scanCard(action.session, action.chainId)
val scanResponse = store.state.globalState.scanResponse ?: return
scanCard(scanResponse, action.session, action.chainId)
}
is WalletConnectAction.ApproveSession -> {
walletConnectManager.approve(action.session)
@ -150,7 +157,6 @@ class WalletConnectMiddleware {
data = messageData,
session = action.sessionData.session,
sessionId = action.id,
cardId = action.sessionData.wallet.cardId,
dAppName = action.sessionData.peerMeta.name,
),
),
@ -164,7 +170,6 @@ class WalletConnectMiddleware {
data = messageData,
session = action.sessionData.session,
sessionId = action.id,
cardId = action.sessionData.wallet.cardId,
dAppName = action.sessionData.peerMeta.name,
),
),
@ -189,28 +194,26 @@ class WalletConnectMiddleware {
currenciesRepository = currenciesRepository,
)
val walletState = store.state.walletState
scope.launch {
val walletManager = factory.getWalletManager(
wallet = action.session.wallet,
blockchain = blockchain,
walletState = walletState,
).guard {
store.dispatchOnMain(
GlobalAction.ShowDialog(
WalletConnectDialog.AddNetwork(blockchain.fullName),
),
)
return@launch
}
val updatedWallet = action.session.wallet.copy(
walletPublicKey = walletManager.wallet.publicKey.seedKey,
derivedPublicKey = walletManager.wallet.publicKey.derivedKey,
derivationPath = walletManager.wallet.publicKey.derivationPath,
blockchain = action.blockchain,
val walletManager = factory.getWalletManager(
wallet = action.session.wallet,
blockchain = blockchain,
walletState = walletState,
).guard {
store.dispatchOnMain(
GlobalAction.ShowDialog(
WalletConnectDialog.AddNetwork(blockchain.fullName),
),
)
val updatedSession = action.session.copy(wallet = updatedWallet)
store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession))
return
}
val updatedWallet = action.session.wallet.copy(
walletPublicKey = walletManager.wallet.publicKey.seedKey,
derivedPublicKey = walletManager.wallet.publicKey.derivedKey,
derivationPath = walletManager.wallet.publicKey.derivationPath,
blockchain = action.blockchain,
)
val updatedSession = action.session.copy(wallet = updatedWallet)
store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession))
}
is WalletConnectAction.UpdateBlockchain -> {
walletConnectManager.updateBlockchain(action.updatedSession)
@ -218,24 +221,16 @@ class WalletConnectMiddleware {
}
}
private fun scanCard(session: WalletConnectSession, chainId: Int?) {
private fun scanCard(scanResponse: ScanResponse, session: WalletConnectSession, chainId: Int?) {
val blockchain = WalletConnectNetworkUtils.parseBlockchain(
chainId = chainId,
peer = session.peerMeta,
) ?: Blockchain.Ethereum
).guard {
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork))
return
}
store.dispatch(
GlobalAction.ScanCard(
additionalBlockchainsToDerive = listOf(blockchain),
onSuccess = { scanResponse ->
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
},
onFailure = {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
},
R.string.wallet_connect_scan_card_message,
),
)
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
}
private suspend fun getAvailableBlockchains(card: Card, walletState: WalletState): List<Blockchain> {
@ -259,6 +254,7 @@ class WalletConnectMiddleware {
walletState: WalletState,
blockchain: Blockchain,
session: WalletConnectSession,
walletConnectManager: WalletConnectManager,
) {
val factory = WcWalletManagerFactory(
factory = store.state.globalState.tapWalletManager.walletManagerFactory,
@ -273,21 +269,20 @@ class WalletConnectMiddleware {
)
return
}
val wallet = walletManager.wallet
val derivedKey =
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
null
} else {
walletManager.wallet.publicKey.blockchainKey
}
val walletForSession = WalletForSession(
cardId = scanResponse.card.cardId,
walletPublicKey = wallet.publicKey.seedKey,
derivedPublicKey = derivedKey,
derivationPath = wallet.publicKey.derivationPath,
derivationStyle = scanResponse.card.derivationStyle,
blockchain = wallet.blockchain,
)
val wallet = walletManager.wallet
val derivedKey =
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
null
} else {
walletManager.wallet.publicKey.blockchainKey
}
val walletForSession = WalletForSession(
walletPublicKey = wallet.publicKey.seedKey,
derivedPublicKey = derivedKey,
derivationPath = wallet.publicKey.derivationPath,
derivationStyle = scanResponse.card.derivationStyle,
blockchain = wallet.blockchain,
)
withMainContext {
val updatedSession = session.copy(wallet = walletForSession)

View file

@ -10,6 +10,7 @@ class WalletConnectReducer {
if (action !is WalletConnectAction) return state
return when (action) {
is WalletConnectAction.ResetState -> return WalletConnectState()
is WalletConnectAction.ApproveSession.Success -> {
state.copy(
loading = false,

View file

@ -40,7 +40,6 @@ data class WalletConnectSession(
@JsonClass(generateAdapter = true)
data class WalletForSession(
val cardId: String,
val walletPublicKey: ByteArray? = null,
val derivedPublicKey: ByteArray? = null,
val derivationPath: DerivationPath? = null,
@ -59,7 +58,6 @@ data class WalletForSession(
other as WalletForSession
if (cardId != other.cardId) return false
if (walletPublicKey != null) {
if (other.walletPublicKey == null) return false
if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false
@ -76,8 +74,7 @@ data class WalletForSession(
}
override fun hashCode(): Int {
var result = cardId.hashCode()
result = 31 * result + (walletPublicKey?.contentHashCode() ?: 0)
var result = (walletPublicKey?.contentHashCode() ?: 0)
result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0)
result = 31 * result + (derivationPath?.hashCode() ?: 0)
result = 31 * result + isTestNet.hashCode()
@ -112,7 +109,6 @@ sealed class WalletConnectDialog : StateDialog {
val data: BinanceMessageData,
val session: WCSession,
val sessionId: Long,
val cardId: String,
val dAppName: String,
) : WalletConnectDialog()
}

View file

@ -1,55 +0,0 @@
package com.tangem.tap.features.details.ui.walletconnect
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
import com.tangem.tap.store
import com.tangem.wallet.R
class WalletConnectSessionsAdapter
: ListAdapter<WalletConnectSession, WalletConnectSessionsAdapter.SessionsViewHolder>(
DiffUtilCallback
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SessionsViewHolder {
val layout = LayoutInflater.from(parent.context)
.inflate(R.layout.item_wallet_connect_session, parent, false)
return SessionsViewHolder(layout)
}
override fun onBindViewHolder(holder: SessionsViewHolder, position: Int) {
holder.bind(currentList[position])
}
object DiffUtilCallback : DiffUtil.ItemCallback<WalletConnectSession>() {
override fun areContentsTheSame(
oldItem: WalletConnectSession, newItem: WalletConnectSession,
) = oldItem == newItem
override fun areItemsTheSame(
oldItem: WalletConnectSession, newItem: WalletConnectSession,
) = oldItem == newItem
}
class SessionsViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
fun bind(session: WalletConnectSession) {
view.findViewById<TextView>(R.id.tv_card_id).text = view.context.getString(
R.string.wallet_connect_card_number, session.wallet.cardId
)
view.findViewById<TextView>(R.id.tv_d_app_name).text = session.peerMeta.name
view.findViewById<Chip>(R.id.btn_disconnect).setOnClickListener {
store.dispatch(WalletConnectAction.DisconnectSession(session.session))
}
}
}
}

View file

@ -169,7 +169,6 @@ fun WalletConnectScreenPreview() {
sessions = listOf(
WcSessionForScreen(
description = "session from some dApp",
cardId = "12312312321",
sessionId = "",
),
),

View file

@ -11,14 +11,12 @@ data class WalletConnectScreenState(
data class WcSessionForScreen(
val description: String,
val cardId: String,
val sessionId: String,
) {
companion object {
fun fromSession(session: WalletConnectSession): WcSessionForScreen {
return WcSessionForScreen(
description = session.peerMeta.name,
cardId = session.wallet.cardId,
sessionId = session.session.toUri(),
)
}

View file

@ -14,7 +14,6 @@ class ApproveWcSessionDialog {
fun create(session: WalletConnectSession, networks: List<Blockchain>, context: Context): AlertDialog {
val message = context.getString(
R.string.wallet_connect_request_session_start,
session.wallet.cardId,
session.peerMeta.name,
session.peerMeta.url,
)

View file

@ -15,7 +15,6 @@ class BnbTransactionDialog {
data: BinanceMessageData,
session: WCSession,
sessionId: Long,
cardId: String,
dAppName: String,
context: Context,
): AlertDialog {
@ -40,7 +39,7 @@ class BnbTransactionDialog {
val fullMessage = context.getString(
R.string.wallet_connect_bnb_sign_message,
dAppName, cardId, message
dAppName, message,
)
val positiveButtonTitle = context.getText(R.string.common_sign)

View file

@ -15,10 +15,12 @@ class PersonalSignDialog {
context: Context,
): AlertDialog {
val message =
context.getString(R.string.wallet_connect_alert_sign_message, data.cardId) +
context.getString(R.string.wallet_connect_personal_sign_message,
data.dAppName,
data.message)
context.getString(R.string.wallet_connect_alert_sign_message) +
context.getString(
R.string.wallet_connect_personal_sign_message,
data.dAppName,
data.message,
)
return AlertDialog.Builder(context).apply {
setTitle(context.getString(R.string.wallet_connect))
setMessage(message)
@ -37,7 +39,6 @@ class PersonalSignDialog {
}
data class PersonalSignDialogData(
val cardId: String,
val dAppName: String,
val message: String,
val session: WCSession,

View file

@ -17,7 +17,6 @@ class TransactionDialog {
): AlertDialog {
val message = context.getString(
R.string.wallet_connect_create_tx_message,
data.cardId,
data.dAppName,
data.dAppUrl,
data.amount,
@ -53,7 +52,6 @@ class TransactionDialog {
}
data class TransactionRequestDialogData(
val cardId: String,
val dAppName: String,
val dAppUrl: String,
val amount: String,

View file

@ -45,7 +45,6 @@ data class ButtonData(
interface DialogMessageData
data class WcTransactionDialogMessageData(
val cardId: String,
val dAppName: String,
val dAppUrl: String,
val amount: String,

View file

@ -4,9 +4,9 @@ import com.tangem.Message
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.AssetReader
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import org.rekotlin.Action

View file

@ -15,10 +15,10 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.navigation.AppScreen
@ -26,7 +26,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
@ -46,13 +45,6 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private lateinit var twinsWidget: TwinsCardWidget
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String =
requireContext().readAssetAsString(name)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@ -250,7 +242,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
store.dispatch(
TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
assetReader
AndroidAssetReader(requireContext()),
)
)
}

View file

@ -120,6 +120,7 @@ private fun filterErrorsForAmountField(errors: EnumSet<TransactionError>): EnumS
TransactionError.TotalExceedsBalance -> {
val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance)
if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it)
showIntoAmountField.remove(TransactionError.AmountLowerExistentialDeposit)
}
else -> showIntoAmountField.add(it)
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.Amount
@ -301,12 +302,21 @@ fun createValidateTransactionError(
val tapErrors = errorList.map {
when (it) {
TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance
TransactionError.AmountLowerExistentialDeposit -> {
if (walletManager is ExistentialDepositProvider) {
val args = listOf(walletManager.getExistentialDeposit().stripZeroPlainString())
TapError.AmountLowerExistentialDeposit(args)
} else {
TapError.UnknownError
}
}
TransactionError.FeeExceedsBalance -> TapError.FeeExceedsBalance
TransactionError.TotalExceedsBalance -> TapError.TotalExceedsBalance
TransactionError.InvalidAmountValue -> TapError.InvalidAmountValue
TransactionError.InvalidFeeValue -> TapError.InvalidFeeValue
TransactionError.DustAmount -> {
TapError.DustAmount(listOf(walletManager.dustValue?.stripZeroPlainString() ?: "0"))
val args = listOf(walletManager.dustValue?.stripZeroPlainString() ?: "0")
TapError.DustAmount(args)
}
TransactionError.DustChange -> TapError.DustChange
else -> TapError.UnknownError

View file

@ -218,7 +218,7 @@ class ReceiptReducer : SendInternalReducer {
}
private fun String.addPrecisionSign(): String {
val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else ""
val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else this
return result.trim()
}
}

View file

@ -3,8 +3,19 @@ package com.tangem.tap.features.send.redux.reducers
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.features.send.redux.SendScreenAction
import com.tangem.tap.features.send.redux.TransactionExtrasAction.*
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.send.redux.TransactionExtrasAction.BinanceMemo
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Prepare
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Release
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XlmMemo
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XrpDestinationTag
import com.tangem.tap.features.send.redux.states.BinanceMemoState
import com.tangem.tap.features.send.redux.states.InputViewValue
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.send.redux.states.TransactionExtraError
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.send.redux.states.XlmMemoState
import com.tangem.tap.features.send.redux.states.XlmMemoType
import com.tangem.tap.features.send.redux.states.XrpDestinationTagState
/**
[REDACTED_AUTHOR]
@ -53,42 +64,33 @@ class TransactionExtrasReducer : SendInternalReducer {
}
private fun handleXlmMemo(
action: XlmMemo,
sendState: SendState,
infoState: TransactionExtrasState,
action: XlmMemo,
sendState: SendState,
infoState: TransactionExtrasState,
): SendState {
fun clearMemo(memo: XlmMemoState): XlmMemoState = memo.copy(text = null, id = null, error = null)
val result = when (action) {
// is XlmMemo.ChangeSelectedMemo -> {
// val inputViewValue = InputViewValue("", false)
// val memo = infoState.xlmMemo?.copy(
// viewFieldValue = inputViewValue,
// selectedMemoType = action.memoType,
// ) ?: XlmMemoState(inputViewValue, action.memoType)
//
// infoState.copy(xlmMemo = clearMemo(memo))
// }
is XlmMemo.HandleUserInput -> {
val inputViewValue = InputViewValue(action.data, true)
var memo = infoState.xlmMemo?.copy(viewFieldValue = inputViewValue)
?: XlmMemoState(inputViewValue)
?: XlmMemoState(inputViewValue)
memo = clearMemo(memo)
memo = when (infoState.xlmMemo?.selectedMemoType) {
XlmMemoType.TEXT -> memo.copy(text = StellarMemo.Text(action.data))
XlmMemoType.ID -> {
val id = action.data.toBigIntegerOrNull()
if (id != null) {
if (id > XlmMemoState.MAX_NUMBER) {
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
} else {
memo.copy(id = StellarMemo.Id(id))
}
memo = when (memo.selectedMemoType) {
XlmMemoType.TEXT -> {
if (XlmMemoState.isAssignableValue(action.data)) {
memo.copy(text = StellarMemo.Text(action.data))
} else {
memo
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
}
}
XlmMemoType.ID -> {
if (XlmMemoState.isAssignableValue(action.data)) {
memo.copy(id = StellarMemo.Id(action.data.toBigInteger()))
} else {
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
}
}
null -> memo
}
infoState.copy(xlmMemo = memo)
}
@ -117,16 +119,16 @@ class TransactionExtrasReducer : SendInternalReducer {
}
private fun handleXrpTag(
action: XrpDestinationTag,
sendState: SendState,
infoState: TransactionExtrasState,
action: XrpDestinationTag,
sendState: SendState,
infoState: TransactionExtrasState,
): SendState {
val result = when (action) {
is XrpDestinationTag.HandleUserInput -> {
val tag = action.data.toLongOrNull()
if (tag != null) {
val input = InputViewValue(action.data, true)
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER){
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER) {
XrpDestinationTagState(input, tag)
} else {
XrpDestinationTagState(input, error = TransactionExtraError.INVALID_DESTINATION_TAG)

View file

@ -1,19 +1,20 @@
package com.tangem.tap.features.send.redux.states
import androidx.core.text.isDigitsOnly
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
import java.math.BigInteger
data class AddressPayIdState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val normalFieldValue: String? = null,
val truncatedFieldValue: String? = null,
val destinationWalletAddress: String? = null,
val error: AddressPayIdVerifyAction.Error? = null,
val truncateHandler: ((String) -> String)? = null,
val sendingToPayIdEnabled: Boolean = false,
val pasteIsEnabled: Boolean = false,
val inputIsEnabled: Boolean = true
val viewFieldValue: InputViewValue = InputViewValue(""),
val normalFieldValue: String? = null,
val truncatedFieldValue: String? = null,
val destinationWalletAddress: String? = null,
val error: AddressPayIdVerifyAction.Error? = null,
val truncateHandler: ((String) -> String)? = null,
val sendingToPayIdEnabled: Boolean = false,
val pasteIsEnabled: Boolean = false,
val inputIsEnabled: Boolean = true,
) : SendScreenState {
override val stateId: StateId = StateId.ADDRESS_PAY_ID
@ -26,9 +27,9 @@ data class AddressPayIdState(
}
data class TransactionExtrasState(
val xlmMemo: XlmMemoState? = null,
val binanceMemo: BinanceMemoState? = null,
val xrpDestinationTag: XrpDestinationTagState? = null
val xlmMemo: XlmMemoState? = null,
val binanceMemo: BinanceMemoState? = null,
val xrpDestinationTag: XrpDestinationTagState? = null,
) : IdStateHolder {
override val stateId: StateId = StateId.TRANSACTION_EXTRAS
}
@ -38,11 +39,10 @@ enum class XlmMemoType {
}
data class XlmMemoState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val selectedMemoType: XlmMemoType = XlmMemoType.ID,
val text: StellarMemo.Text? = null,
val id: StellarMemo.Id? = null,
val error: TransactionExtraError? = null,
val viewFieldValue: InputViewValue = InputViewValue(""),
val text: StellarMemo.Text? = null,
val id: StellarMemo.Id? = null,
val error: TransactionExtraError? = null,
) {
val memo: StellarMemo?
get() = when (selectedMemoType) {
@ -50,16 +50,37 @@ data class XlmMemoState(
XlmMemoType.ID -> id
}
val selectedMemoType: XlmMemoType
get() = determineMemoType(viewFieldValue.value)
companion object {
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
fun determineMemoType(value: String): XlmMemoType = when {
value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID
else -> XlmMemoType.TEXT
}
fun isAssignableValue(value: String): Boolean = when (determineMemoType(value)) {
XlmMemoType.TEXT -> {
// from org.stellar.sdk.MemoText
value.toByteArray().size <= 28
}
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
}
}
}
}
}
data class BinanceMemoState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val memo: BigInteger? = null,
val error: TransactionExtraError? = null
) {
val error: TransactionExtraError? = null,
) {
companion object {
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
}
@ -67,9 +88,9 @@ data class BinanceMemoState(
// tag must contains only digits
data class XrpDestinationTagState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val tag: Long? = null,
val error: TransactionExtraError? = null
val viewFieldValue: InputViewValue = InputViewValue(""),
val tag: Long? = null,
val error: TransactionExtraError? = null,
) {
companion object {
const val MAX_NUMBER: Long = 4294967295

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.module.ModuleMessageConverter
import com.tangem.tangem_sdk_new.extensions.localizedDescription
@ -47,9 +48,13 @@ private class BlockchainSdkErrorConverter(
override fun convert(message: BlockchainSdkError): String {
return when (message) {
is BlockchainSdkError.CreateAccountUnderfunded -> {
val reserve = message.minReserve.value?.stripZeroPlainString() ?: "0"
val symbol = message.minReserve.currencySymbol
context.getString(R.string.send_error_no_target_account, reserve, symbol)
val resStringId = when (message.blockchain) {
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.string.no_account_polkadot
else -> R.string.send_error_no_target_account
}
val reserveValueString = message.minReserve.value?.stripZeroPlainString() ?: "0"
val argument = "$reserveValueString ${message.minReserve.currencySymbol}"
context.getString(resStringId, argument)
}
else -> message.customMessage
}

View file

@ -2,7 +2,6 @@ package com.tangem.tap.features.send.ui.stateSubscribers
import android.app.Dialog
import android.content.Context
import android.text.InputType
import android.text.SpannableStringBuilder
import android.view.View
import android.view.ViewGroup
@ -31,7 +30,6 @@ import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.send.redux.states.StateId
import com.tangem.tap.features.send.redux.states.TransactionExtraError
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.send.redux.states.XlmMemoType
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
@ -80,14 +78,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
showView(binanceMemoContainer, infoState.binanceMemo)
infoState.xlmMemo?.let {
etXlmMemo.inputType = when (it.selectedMemoType) {
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
}
if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value)
if (it.error != null) {
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
tilXlmMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
}
} else {
tilXlmMemo.error = null
@ -97,7 +91,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
if (infoState.xrpDestinationTag.error != null) {
if (infoState.xrpDestinationTag.error == TransactionExtraError.INVALID_DESTINATION_TAG) {
tilDestinationTag.error =
fg.getText(R.string.send_error_invalid_destination_tag)
fg.getText(R.string.send_extras_error_invalid_destination_tag)
}
} else {
tilDestinationTag.error = null
@ -109,7 +103,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
infoState.binanceMemo?.let {
if (infoState.binanceMemo.error != null) {
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
tilBinanceMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
}
} else {
tilBinanceMemo.error = null