Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-13 12:51:12 +03:00
parent 158fd355de
commit 473aeee675
18 changed files with 1176 additions and 226 deletions

View file

@ -267,29 +267,45 @@ internal class SendModel @Inject constructor(
private suspend fun prepareTransferTransaction(): Either<Throwable, TransactionData> {
val predefinedValues = predefinedValues
val cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value
return if (predefinedValues is PredefinedValues.Content.Deeplink) {
val predefinedAmount = predefinedValues.amount.parseBigDecimalOrNull()
createTransferTransactionUseCase(
amount = predefinedAmount?.convertToSdkAmount(cryptoCurrencyStatus) ?: error("Invalid amount"),
memo = predefinedValues.memo,
destination = predefinedValues.address,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
} else {
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
val amountUM = uiState.value.amountUM as? AmountState.Data ?: error("Invalid amount")
val enteredDestinationAddress = destinationUM.addressTextField.actualAddress
val enteredMemo = destinationUM.memoTextField?.value
val enteredAmount = amountUM.amountTextField.cryptoAmount.value ?: error("Invalid amount")
return when (predefinedValues) {
is PredefinedValues.Content.Deeplink -> {
val predefinedAmount = predefinedValues.amount.parseBigDecimalOrNull()
createTransferTransactionUseCase(
amount = predefinedAmount?.convertToSdkAmount(cryptoCurrencyStatus)
?: error("Invalid amount"),
memo = predefinedValues.memo,
destination = predefinedValues.address,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
}
is PredefinedValues.Content.QrCode -> {
val predefinedAmount = predefinedValues.amount?.parseBigDecimalOrNull()
createTransferTransactionUseCase(
amount = predefinedAmount?.convertToSdkAmount(cryptoCurrencyStatus)
?: error("Invalid amount"),
memo = predefinedValues.memo,
destination = predefinedValues.address,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
}
PredefinedValues.Empty -> {
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content
?: error("Invalid destination")
val amountUM = uiState.value.amountUM as? AmountState.Data ?: error("Invalid amount")
val enteredDestinationAddress = destinationUM.addressTextField.actualAddress
val enteredMemo = destinationUM.memoTextField?.value
val enteredAmount = amountUM.amountTextField.cryptoAmount.value ?: error("Invalid amount")
createTransferTransactionUseCase(
amount = enteredAmount.convertToSdkAmount(cryptoCurrencyStatus),
memo = enteredMemo,
destination = enteredDestinationAddress,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
createTransferTransactionUseCase(
amount = enteredAmount.convertToSdkAmount(cryptoCurrencyStatus),
memo = enteredMemo,
destination = enteredDestinationAddress,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
}
}
}
@ -348,6 +364,12 @@ internal class SendModel @Inject constructor(
memo = params.tag,
transactionId = predefinedTxId,
)
} else if (predefinedAddress != null) {
PredefinedValues.Content.QrCode(
amount = predefinedAmount,
address = predefinedAddress,
memo = params.tag,
)
} else {
PredefinedValues.Empty
}

View file

@ -96,7 +96,7 @@ internal class SendDestinationModel @Inject constructor(
),
)
val params = params as? DestinationBlockParams
val predefinedValues = params?.predefinedValues as? PredefinedValues.Content.Deeplink
val predefinedValues = params?.predefinedValues as? PredefinedValues.Content
if (predefinedValues?.address != null) {
_uiState.update(
SendDestinationPredefinedStateTransformer(

View file

@ -5,6 +5,7 @@ import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
@ -25,13 +26,12 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.qrscanning.models.QrResultSource
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.qrscanning.usecases.ClassifyQrCodeUseCase
import com.tangem.domain.qrscanning.usecases.ResolveQrSendTargetsUseCase
import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.wallets.usecase.*
@ -40,7 +40,6 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.domain.qrscanning.models.ClassifiedQrContent
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletContentFetcher
@ -115,8 +114,7 @@ internal class WalletModel @Inject constructor(
private val walletFeatureToggles: WalletFeatureToggles,
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val wcPairService: WcPairService,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val classifyQrCodeUseCase: ClassifyQrCodeUseCase,
private val resolveQrSendTargetsUseCase: ResolveQrSendTargetsUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -752,16 +750,8 @@ internal class WalletModel @Inject constructor(
}
private suspend fun handleQrResult(qrCode: String, resultSource: QrResultSource) {
val userWalletId = stateHolder.getSelectedWalletId()
val currencies = multiWalletCryptoCurrenciesSupplier
.getSyncOrNull(MultiWalletCryptoCurrenciesProducer.Params(userWalletId))
?.toList()
.orEmpty()
val classified = classifyQrCodeUseCase(qrCode, currencies)
when (classified) {
is ClassifiedQrContent.WalletConnect -> {
when (val target = resolveQrSendTargetsUseCase(qrCode)) {
is QrSendTarget.WalletConnect -> {
val source = when (resultSource) {
QrResultSource.CLIPBOARD -> WcPairRequest.Source.CLIPBOARD
QrResultSource.CAMERA,
@ -770,34 +760,25 @@ internal class WalletModel @Inject constructor(
}
wcPairService.pair(
WcPairRequest(
userWalletId = userWalletId,
uri = classified.uri,
userWalletId = stateHolder.getSelectedWalletId(),
uri = target.uri,
source = source,
),
)
}
is ClassifiedQrContent.PaymentUri -> {
is QrSendTarget.Single -> {
innerWalletRouter.openSend(
userWalletId = userWalletId,
currency = classified.currency,
address = classified.address,
amount = classified.amount?.toPlainString(),
tag = classified.memo,
userWalletId = target.userWalletId,
currency = target.currency,
address = target.address,
amount = target.amount?.parseBigDecimal(target.currency.decimals),
tag = target.memo,
)
}
is ClassifiedQrContent.PlainAddress -> {
if (classified.matchingCurrencies.size == 1) {
innerWalletRouter.openSend(
userWalletId = userWalletId,
currency = classified.matchingCurrencies.first(),
address = classified.address,
amount = null,
tag = null,
)
}
// TODO: [REDACTED_TASK_KEY] Network selection bottom sheet for multiple network matches
is QrSendTarget.Multiple -> {
// TODO: [REDACTED_TASK_KEY] Bottom sheet: Wallets (dropdown) → Accounts → Tokens
}
is ClassifiedQrContent.Unknown -> {
is QrSendTarget.Unknown -> {
// TODO: [REDACTED_TASK_KEY] Error handling for unsupported and invalid QR codes
}
}