Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-16 11:12:07 +03:00
commit 8adc79a8c3
12 changed files with 89 additions and 81 deletions

View file

@ -1,5 +1,6 @@
package com.tangem.tap.domain.walletconnect2.domain
import com.tangem.blockchain.blockchains.solana.SolanaTransactionHelper
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
import com.tangem.tap.domain.walletconnect2.domain.models.BnbData
@ -150,10 +151,8 @@ internal class WcSessionRequestConverter(
* Input transaction in Base64 string
*/
private fun String.prepareSolanaTransaction(): ByteArray {
return this.decodeBase64()
?.toByteArray()
?.drop(SOLANA_SIGNATURE_PLACEHOLDER_LENGTH)
?.toByteArray() ?: ByteArray(0)
val transaction = this.decodeBase64()?.toByteArray() ?: ByteArray(0)
return SolanaTransactionHelper.removeSignaturesPlaceholders(transaction)
}
private fun getWalletAddress(request: WcRequest): String? {
@ -182,8 +181,4 @@ internal class WcSessionRequestConverter(
(walletAddress == null || it.walletAddress.lowercase() == walletAddress.lowercase())
}?.derivationPath
}
companion object {
private const val SOLANA_SIGNATURE_PLACEHOLDER_LENGTH = 65
}
}

View file

@ -1,9 +1,7 @@
package com.tangem.core.ui.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
/**
@ -13,24 +11,18 @@ import com.google.accompanist.permissions.rememberPermissionState
@Suppress("LongParameterList")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun requestPushPermission(
pushPermission: String?,
isClicked: Boolean,
onAllow: () -> Unit,
onDeny: () -> Unit,
): () -> Unit {
fun requestPushPermission(pushPermission: String?, onAllow: () -> Unit, onDeny: () -> Unit): () -> Unit {
val permissionState = pushPermission?.let { permission ->
rememberPermissionState(permission = permission)
}
// Check if user granted permission and close bottom sheet
LaunchedEffect(key1 = permissionState?.status, isClicked) {
if (!isClicked) return@LaunchedEffect
if (permissionState?.status?.isGranted == true) {
onAllow()
} else {
onDeny()
}
rememberPermissionState(
permission = permission,
onPermissionResult = { isGranted ->
if (isGranted) {
onAllow()
} else {
onDeny()
}
},
)
}
return if (permissionState == null) {

View file

@ -7,6 +7,8 @@ import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.blockchain.common.transaction.TransactionsSendResult
import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import java.math.BigDecimal
import java.math.BigInteger
import kotlin.random.Random
class DemoTransactionSender(private val walletManager: WalletManager) : TransactionSender {
@ -15,9 +17,9 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
val blockchain = walletManager.wallet.blockchain
return Result.Success(
TransactionFee.Choosable(
minimum = Fee.Common(Amount(minimumFee, blockchain)),
normal = Fee.Common(Amount(normalFee, blockchain)),
priority = Fee.Common(Amount(priorityFee, blockchain)),
minimum = blockchain.getStubFee(minimumFee),
normal = blockchain.getStubFee(normalFee),
priority = blockchain.getStubFee(priorityFee),
),
)
}
@ -44,7 +46,16 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
transactionDataList: List<TransactionData>,
signer: TransactionSigner,
sendMode: TransactionSender.MultipleTransactionSendMode,
): Result<TransactionsSendResult> = Result.Failure(Exception(ID).toBlockchainSdkError())
): Result<TransactionsSendResult> {
val signerResponse = signer.sign(
hash = getDataToSign(),
publicKey = walletManager.wallet.publicKey,
)
return when (signerResponse) {
is CompletionResult.Success -> Result.Failure(Exception(ID).toBlockchainSdkError())
is CompletionResult.Failure -> Result.fromTangemSdkError(signerResponse.error)
}
}
private fun getDataToSign(): ByteArray {
val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
@ -55,6 +66,23 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
.toByteArray()
}
private fun Blockchain.getStubFee(fee: BigDecimal) = when (this) {
Blockchain.Ethereum -> Fee.Ethereum.EIP1559(
amount = Amount(fee, this),
gasLimit = BigInteger.ONE,
maxFeePerGas = BigInteger.ONE,
priorityFee = BigInteger.ONE,
)
Blockchain.Dogecoin,
Blockchain.Bitcoin,
-> Fee.Bitcoin(
amount = Amount(fee, this),
satoshiPerByte = BigDecimal.ONE,
txSize = BigDecimal.ONE,
)
else -> Fee.Common(Amount(normalFee, this))
}
companion object {
val ID: String = DemoTransactionSender::class.java.simpleName

View file

@ -6,7 +6,6 @@ import arrow.core.raise.either
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.txhistory.models.TxStatusError
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import com.tangem.domain.wallets.models.UserWalletId
class GetExplorerTransactionUrlUseCase(
private val repository: TxHistoryRepository,
@ -23,17 +22,4 @@ class GetExplorerTransactionUrlUseCase(
)
}
}
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<TxStatusError, String> {
return either {
catch(
block = {
repository.getTxExploreUrl(userWalletId, network).ifEmpty {
raise(TxStatusError.EmptyUrlError)
}
},
catch = { raise(TxStatusError.DataError(it)) },
)
}
}
}

View file

@ -19,9 +19,7 @@ internal fun PushNotificationsScreen(
onAllowPermission: () -> Unit,
onDenyPermission: () -> Unit,
) {
var isClicked by remember { mutableStateOf(false) }
val requestPushPermission = requestPushPermission(
isClicked = isClicked,
onAllow = onAllowPermission,
onDeny = onDenyPermission,
pushPermission = getPushPermissionOrNull(),
@ -43,7 +41,6 @@ internal fun PushNotificationsScreen(
primaryButton = ShowcaseButtonModel(
buttonText = resourceReference(R.string.common_allow),
onClick = {
isClicked = true
onRequest()
requestPushPermission()
},

View file

@ -24,6 +24,7 @@ import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.SendContent
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
import com.tangem.features.send.v2.send.model.SendModel
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
@ -161,7 +162,20 @@ internal class DefaultSendComponent @AssistedInject constructor(
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
callback = model,
predefinedValues = model.predefinedValues,
onBackClick = ::onChildBack,
onBackClick = {
if (route.isEditMode) {
onChildBack()
} else {
analyticsEventHandler.send(
SendAnalyticEvents.CloseButtonClicked(
source = SendScreenSource.Amount,
isFromSummary = false,
isValid = model.uiState.value.amountUM.isPrimaryButtonEnabled,
),
)
router.pop()
}
},
onNextClick = {
if (route.isEditMode) {
onChildBack()

View file

@ -378,10 +378,10 @@ internal class SendConfirmModel @Inject constructor(
}
}
private suspend fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
private fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
val txUrl = getExplorerTransactionUrlUseCase(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
txHash = txData.hash.orEmpty(),
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
_uiState.update(SendConfirmSentStateTransformer(txData, txUrl))
}

View file

@ -223,13 +223,12 @@ internal class SendAmountModel @Inject constructor(
NavigationUM.Content(
title = resourceReference(R.string.send_amount_label),
subtitle = null,
backIconRes = R.drawable.ic_back_24,
backIconClick = {
if (!route.isEditMode) {
saveResult()
}
params.onBackClick()
backIconRes = if (route.isEditMode) {
R.drawable.ic_back_24
} else {
R.drawable.ic_close_24
},
backIconClick = { params.onBackClick() },
primaryButton = ButtonsUM.PrimaryButtonUM(
text = if (route.isEditMode) {
resourceReference(R.string.common_continue)

View file

@ -78,22 +78,11 @@ internal class SendDestinationModel @Inject constructor(
private var validationJobHolder = JobHolder()
init {
initSenderAddress()
configDestinationNavigation()
subscribeOnQRScannerResult()
initialState()
}
private fun initSenderAddress() {
modelScope.launch {
senderAddresses.value = getNetworkAddressesUseCase.invokeSync(userWalletId, cryptoCurrency.network)
.filter { cryptoCurrency.id == it.cryptoCurrency.id }
}
senderAddresses.onEach {
getWalletsAndRecent()
}.launchIn(modelScope)
}
private fun initialState() {
if ((uiState.value as? DestinationUM.Content)?.isInitialized == false || uiState.value is DestinationUM.Empty) {
_uiState.update(
@ -112,6 +101,7 @@ internal class SendDestinationModel @Inject constructor(
),
)
}
initSenderAddress()
}
}
@ -150,6 +140,16 @@ internal class SendDestinationModel @Inject constructor(
)
}
private fun initSenderAddress() {
modelScope.launch {
senderAddresses.value = getNetworkAddressesUseCase.invokeSync(userWalletId, cryptoCurrency.network)
.filter { cryptoCurrency.id == it.cryptoCurrency.id }
}
senderAddresses.onEach {
getWalletsAndRecent()
}.launchIn(modelScope)
}
private fun subscribeOnQRScannerResult() {
listenToQrScanningUseCase(SourceType.SEND)
.getOrElse { emptyFlow() }

View file

@ -515,13 +515,13 @@ internal class SendModel @Inject constructor(
when (currentState.type) {
SendUiStateType.Fee,
SendUiStateType.EditFee,
-> if (uiState.value.feeState?.isPrimaryButtonEnabled == false) return
-> if (uiState.value.getFeeState(isFromEdit)?.isPrimaryButtonEnabled == false) return
SendUiStateType.Amount,
SendUiStateType.EditAmount,
-> if (!uiState.value.amountState.isPrimaryButtonEnabled) return
-> if (!uiState.value.getAmountState(isFromEdit).isPrimaryButtonEnabled) return
SendUiStateType.Recipient,
SendUiStateType.EditRecipient,
-> if (uiState.value.recipientState?.isPrimaryButtonEnabled == false) return
-> if (uiState.value.getRecipientState(isFromEdit)?.isPrimaryButtonEnabled == false) return
SendUiStateType.Send -> if (uiState.value.sendState?.isPrimaryButtonEnabled == false) return
SendUiStateType.None -> return
}
@ -995,10 +995,10 @@ internal class SendModel @Inject constructor(
}
}
private suspend fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
private fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
val txUrl = getExplorerTransactionUrlUseCase(
userWalletId = userWalletId,
network = cryptoCurrency.network,
txHash = txData.hash.orEmpty(),
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
uiState.value = stateFactory.getTransactionSendState(txData, txUrl)
}

View file

@ -6,13 +6,13 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.showcase.ShowcaseContent
import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel
import com.tangem.core.ui.extensions.resourceReference
@ -34,10 +34,8 @@ internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) {
@Composable
private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) {
var isClicked by remember { mutableStateOf(false) }
val requestPushPermission = requestPushPermission(
pushPermission = getPushPermissionOrNull(),
isClicked = isClicked,
onAllow = {
content.onAllow()
onDismiss()
@ -84,7 +82,6 @@ private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetC
PrimaryButton(
text = stringResourceSafe(R.string.common_allow),
onClick = {
isClicked = true
content.onRequest()
requestPushPermission()
},

View file

@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1027"
tangemBlockchainSdk = "develop-1033"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-455"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^