Updated on 2026-08-14
This commit is contained in:
parent
b352bb6235
commit
b699b723a8
13 changed files with 99 additions and 69 deletions
|
|
@ -12,12 +12,14 @@ import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
|||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.domain.transaction.usecase.PrepareForSendUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcListTransactionUseCase
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignState
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
|
@ -38,7 +40,7 @@ internal class WcSolanaSignAllTransactionUseCase @AssistedInject constructor(
|
|||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
).map { lce -> lce.map { result -> BlockAidTransactionCheck.Result.Plain(result) } }
|
||||
|
||||
override suspend fun SignCollector<List<TransactionData>>.onSign(state: WcSignState<List<TransactionData>>) {
|
||||
val hash = prepareForSend.invoke(transactionData = state.signModel, userWallet = wallet, network = network)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.tangem.data.walletconnect.request
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||
import com.tangem.domain.walletconnect.model.WcMethod
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
|
||||
import javax.inject.Inject
|
||||
|
|
@ -10,8 +14,10 @@ internal class DefaultWcRequestUseCaseFactory @Inject constructor(
|
|||
) : WcRequestUseCaseFactory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override suspend fun <T : WcMethodUseCase> createUseCase(request: WcSdkSessionRequest): T {
|
||||
return requestConverters.firstNotNullOf { it.toUseCase(request) } as? T
|
||||
?: error("Should check request before in WcRouting")
|
||||
override suspend fun <T : WcMethodUseCase> createUseCase(
|
||||
request: WcSdkSessionRequest,
|
||||
): Either<WcMethod.Unsupported, T> {
|
||||
val useCase = requestConverters.firstNotNullOfOrNull { converter -> converter.toUseCase(request) }
|
||||
return (useCase as? T)?.let { useCase.right() } ?: WcMethod.Unsupported(request).left()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.domain.walletconnect.model
|
||||
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
|
||||
sealed interface WcMethod {
|
||||
data object Unsupported : WcMethod
|
||||
data class Unsupported(val request: WcSdkSessionRequest) : WcMethod
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.domain.walletconnect
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.walletconnect.model.WcMethod
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
|
||||
|
||||
interface WcRequestUseCaseFactory {
|
||||
|
||||
suspend fun <T : WcMethodUseCase> createUseCase(request: WcSdkSessionRequest): T
|
||||
suspend fun <T : WcMethodUseCase> createUseCase(request: WcSdkSessionRequest): Either<WcMethod.Unsupported, T>
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ interface WcMethodContext {
|
|||
* Wc Methods that must be signed by Tangem card
|
||||
* [WcSignState] represents signing state
|
||||
*/
|
||||
interface WcSignUseCase<SignModel> : WcMethodContext {
|
||||
interface WcSignUseCase<SignModel> : WcMethodContext, WcMethodUseCase {
|
||||
|
||||
operator fun invoke(): Flow<WcSignState<SignModel>>
|
||||
fun cancel()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.domain.walletconnect.usecase.method
|
|||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.walletconnect.usecase.blockaid.WcBlockAidEligibleTransactionUseCase
|
||||
|
||||
/**
|
||||
* Base UseCase for wc methods with single TransactionData
|
||||
|
|
@ -27,7 +26,7 @@ interface WcTransactionUseCase :
|
|||
*/
|
||||
interface WcListTransactionUseCase :
|
||||
WcSignUseCase<List<TransactionData>>,
|
||||
WcBlockAidEligibleTransactionUseCase
|
||||
BlockAidTransactionCheck
|
||||
|
||||
/**
|
||||
* Wc Methods that support an updatable fee
|
||||
|
|
|
|||
|
|
@ -64,4 +64,5 @@ dependencies {
|
|||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.timber)
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.blockchain)
|
||||
}
|
||||
|
|
@ -15,8 +15,8 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.walletconnect.components.WcRoutingComponent
|
||||
import com.tangem.features.walletconnect.connections.components.WcPairComponent
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponent
|
||||
import com.tangem.features.walletconnect.connections.components.WcPairComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.chain.WcAddNetworkContainerComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.components.send.WcSendTransactionContainerComponent
|
||||
|
|
@ -79,10 +79,10 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
|
|||
source = config.request.source,
|
||||
),
|
||||
)
|
||||
is WcInnerRoute.Alert -> AlertsComponent(
|
||||
is WcInnerRoute.UnsupportedMethodAlert -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = config.alertType,
|
||||
alertType = AlertsComponent.AlertType.UnsupportedMethod { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.walletconnect.connections.routing
|
|||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.domain.walletconnect.model.WcPairRequest
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponent
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
@ -27,5 +26,5 @@ internal sealed interface WcInnerRoute : Route {
|
|||
data class Pair(val request: WcPairRequest) : WcInnerRoute
|
||||
|
||||
@Serializable
|
||||
data class Alert(val alertType: AlertsComponent.AlertType) : WcInnerRoute
|
||||
data class UnsupportedMethodAlert(override val rawRequest: WcSdkSessionRequest) : Method
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import com.tangem.domain.walletconnect.model.WcEthMethodName
|
|||
import com.tangem.domain.walletconnect.model.WcMethodName
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethodName
|
||||
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
|
@ -58,9 +57,7 @@ internal class WcRoutingModel @Inject constructor(
|
|||
WcSolanaMethodName.SendAllTransaction,
|
||||
-> WcInnerRoute.Send(rawRequest)
|
||||
is WcMethodName.Unsupported,
|
||||
-> WcInnerRoute.Alert(
|
||||
alertType = AlertsComponent.AlertType.UnsupportedMethod { innerRouter.pop() },
|
||||
)
|
||||
-> WcInnerRoute.UnsupportedMethodAlert(rawRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import com.tangem.core.decompose.navigation.Router
|
|||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
|
||||
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcAddEthereumChainUMConverter
|
||||
import com.tangem.features.walletconnect.transaction.entity.chain.WcAddEthereumChainUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
|
||||
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcAddEthereumChainUMConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -44,8 +44,10 @@ internal class WcAddNetworkModel @Inject constructor(
|
|||
|
||||
init {
|
||||
modelScope.launch {
|
||||
val useCase: WcMethodUseCase = useCaseFactory.createUseCase(params.rawRequest)
|
||||
val transactionUM = (useCase as? WcAddNetworkUseCase)?.let {
|
||||
val useCase: WcAddNetworkUseCase = useCaseFactory.createUseCase<WcAddNetworkUseCase>(params.rawRequest)
|
||||
.onLeft { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
|
||||
.getOrNull() ?: return@launch
|
||||
_uiState.emit(
|
||||
wcAddEthereumChainUMConverter.convert(
|
||||
WcAddEthereumChainUMConverter.Input(
|
||||
useCase = useCase,
|
||||
|
|
@ -56,9 +58,8 @@ internal class WcAddNetworkModel @Inject constructor(
|
|||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
_uiState.emit(transactionUM)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.walletconnect.transaction.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -12,16 +12,16 @@ import com.tangem.core.ui.clipboard.ClipboardManager
|
|||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||
import com.tangem.domain.walletconnect.usecase.method.*
|
||||
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
|
||||
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
|
||||
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
|
||||
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
|
||||
import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiveBlockAidUiConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -47,52 +47,69 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
val stackNavigation = StackNavigation<WcTransactionRoutes>()
|
||||
|
||||
init {
|
||||
@Suppress("UnusedPrivateMember")
|
||||
modelScope.launch {
|
||||
when (val useCase: WcSignUseCase<TransactionData> = useCaseFactory.createUseCase(params.rawRequest)) {
|
||||
is WcTransactionUseCase,
|
||||
val unknownMethodRunnable = { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
|
||||
val useCase = useCaseFactory.createUseCase<WcSignUseCase<*>>(params.rawRequest)
|
||||
.onLeft { unknownMethodRunnable() }
|
||||
.getOrNull() ?: return@launch
|
||||
when (useCase) {
|
||||
is WcListTransactionUseCase,
|
||||
-> {
|
||||
observeTransactionData(useCase)
|
||||
}
|
||||
is WcTransactionUseCase,
|
||||
-> combine(
|
||||
useCase.invoke(),
|
||||
useCase.securityStatus,
|
||||
) { signState, securityCheck -> signState to securityCheck }
|
||||
.distinctUntilChanged()
|
||||
.collectLatest {
|
||||
val (signState, securityCheck) = it
|
||||
if (signingIsDone(signState)) return@collectLatest
|
||||
val signModel: Any = signState.signModel
|
||||
val isMutableFee = useCase is WcMutableFee
|
||||
val dAppFee = if (isMutableFee) useCase.dAppFee() else null
|
||||
val selectedFee = when (signModel) {
|
||||
is TransactionData.Compiled -> signModel.fee
|
||||
is TransactionData.Uncompiled -> signModel.fee
|
||||
else -> null
|
||||
}
|
||||
val isDAppFeeSelected = dAppFee != null && dAppFee == selectedFee
|
||||
|
||||
val isSecurityCheckContent = securityCheck is Lce.Content
|
||||
var isApprovalMethod = isSecurityCheckContent &&
|
||||
securityCheck.content is BlockAidTransactionCheck.Result.Approval
|
||||
buildUiState(securityCheck, useCase, signState)
|
||||
}
|
||||
else -> unknownMethodRunnable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun observeSecurityData(useCase: WcSignUseCase<TransactionData>) {
|
||||
(useCase as? BlockAidTransactionCheck)?.let {
|
||||
useCase.securityStatus.collectLatest {
|
||||
val blockAidState = when (it) {
|
||||
is Lce.Content -> blockAidUiConverter.convert(it.content.result)
|
||||
is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false)
|
||||
is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true)
|
||||
}
|
||||
val current = _uiState.value
|
||||
_uiState.emit(
|
||||
current?.copy(transaction = current.transaction.copy(estimatedWalletChanges = blockAidState)),
|
||||
)
|
||||
}
|
||||
private suspend fun buildUiState(
|
||||
securityCheck: Lce<Throwable, BlockAidTransactionCheck.Result>,
|
||||
useCase: WcSignUseCase<*>,
|
||||
signState: WcSignState<*>,
|
||||
) {
|
||||
val blockAidState = when (securityCheck) {
|
||||
is Lce.Content -> blockAidUiConverter.convert(securityCheck.content.result)
|
||||
is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false)
|
||||
is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.observeTransactionData(useCase: WcSignUseCase<TransactionData>) {
|
||||
useCase.invoke().onEach { signState ->
|
||||
if (signingIsDone(signState)) return@onEach
|
||||
val transactionUM = converter.convert(
|
||||
WcCommonTransactionUMConverter.Input(
|
||||
useCase = useCase,
|
||||
signState = signState,
|
||||
actions = WcTransactionActionsUM(
|
||||
onShowVerifiedAlert = ::showVerifiedAlert,
|
||||
onDismiss = { cancel(useCase) },
|
||||
onSign = useCase::sign,
|
||||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||
),
|
||||
var transactionUM = converter.convert(
|
||||
WcCommonTransactionUMConverter.Input(
|
||||
useCase = useCase,
|
||||
signState = signState,
|
||||
actions = WcTransactionActionsUM(
|
||||
onShowVerifiedAlert = ::showVerifiedAlert,
|
||||
onDismiss = { cancel(useCase) },
|
||||
onSign = useCase::sign,
|
||||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||
),
|
||||
) as? WcSendTransactionUM
|
||||
_uiState.emit(transactionUM)
|
||||
observeSecurityData(useCase)
|
||||
}
|
||||
.launchIn(this)
|
||||
),
|
||||
) as? WcSendTransactionUM
|
||||
transactionUM = transactionUM?.copy(
|
||||
transaction = transactionUM.transaction.copy(estimatedWalletChanges = blockAidState),
|
||||
)
|
||||
_uiState.emit(transactionUM)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
|
|
|
|||
|
|
@ -9,15 +9,17 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignState
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase
|
||||
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
|
||||
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -46,7 +48,9 @@ internal class WcSignTransactionModel @Inject constructor(
|
|||
|
||||
init {
|
||||
modelScope.launch {
|
||||
val useCase: WcSignUseCase<*> = useCaseFactory.createUseCase(params.rawRequest)
|
||||
val useCase = useCaseFactory.createUseCase<WcMessageSignUseCase>(params.rawRequest)
|
||||
.onLeft { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
|
||||
.getOrNull() ?: return@launch
|
||||
useCase.invoke()
|
||||
.onEach { signState ->
|
||||
if (signingIsDone(signState)) return@onEach
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue