Updated on 2026-08-14
This commit is contained in:
parent
c8a163fd0a
commit
65ace6c5f2
21 changed files with 349 additions and 39 deletions
|
|
@ -1505,12 +1505,16 @@
|
|||
<string name="warning_token_trustline_button_title">Enable Trustline</string>
|
||||
<string name="warning_token_trustline_subtitle">A Trustline must be enabled to receive this token. The network requires a %1$s %2$s reserve.</string>
|
||||
<string name="warning_token_trustline_title">Trustline Required</string>
|
||||
<string name="wc_alert_add_network_to_portfolio_description">The required network %s is not added to your portfolio. Add it first, then proceed with the connection.</string>
|
||||
<string name="wc_alert_add_network_to_portfolio_title">Add network to portfolio</string>
|
||||
<string name="wc_alert_audit_malicious_domain">Malicious domain</string>
|
||||
<string name="wc_alert_audit_unknown_domain">Unknown domain</string>
|
||||
<string name="wc_alert_connect_anyway">Connect anyway</string>
|
||||
<string name="wc_alert_connection_timeout_description">Timeout error. Please, try again later.</string>
|
||||
<string name="wc_alert_connection_timeout_title">Failed to establish WalletConnect</string>
|
||||
<string name="wc_alert_domain_issues_description">This domain cannot be verified. Check the request carefully approving.</string>
|
||||
<string name="wc_alert_network_not_connected_description">To continue, please reconnect your dApp session with the required network %s.</string>
|
||||
<string name="wc_alert_network_not_connected_title">Network not connected</string>
|
||||
<string name="wc_alert_session_disconnected_description">Please return to your browser and reconnect via WalletConnect.</string>
|
||||
<string name="wc_alert_session_disconnected_title">Wallet Connect session was disconnected</string>
|
||||
<string name="wc_alert_sign_anyway">Sign anyway</string>
|
||||
|
|
@ -1521,6 +1525,8 @@
|
|||
<string name="wc_alert_unsupported_dapps_title">Unsupported dApp</string>
|
||||
<string name="wc_alert_unsupported_method_description">Error code: 8 005. If the problem persists — feel free to contact our support.</string>
|
||||
<string name="wc_alert_unsupported_method_title">We\'ve encountered unknown error</string>
|
||||
<string name="wc_alert_unsupported_network_description">This network %s is not supported by Tangem Wallet and cannot be connected.</string>
|
||||
<string name="wc_alert_unsupported_network_title">Unsupported network</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Tangem does not currently support a required network by %s.</string>
|
||||
<string name="wc_alert_unsupported_networks_title">Unsupported networks</string>
|
||||
<string name="wc_alert_verified_domain_description">This domain has passed verification checks and is considered safe, reputable, and free from known threats or suspicious activity. %s</string>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
package com.tangem.data.walletconnect.network.ethereum
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.extensions.hexToInt
|
||||
import com.tangem.data.walletconnect.model.CAIP2
|
||||
import com.tangem.data.walletconnect.network.ethereum.WcEthNetwork.NamespaceConverter.Companion.ETH_NAMESPACE_KEY
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.data.walletconnect.utils.WcNetworksConverter
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.walletconnect.model.HandleMethodError
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.model.WcRequestError
|
||||
import com.tangem.domain.walletconnect.model.WcSession
|
||||
|
|
@ -16,6 +24,7 @@ import dagger.assisted.AssistedInject
|
|||
|
||||
internal class WcEthAddNetworkUseCase @AssistedInject constructor(
|
||||
private val respondService: WcRespondService,
|
||||
addSwitchCommonDelegateFactory: WcEthAddSwitchCommonDelegate.Factory,
|
||||
@Assisted val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcEthMethod.AddEthereumChain,
|
||||
) : WcAddNetworkUseCase {
|
||||
|
|
@ -31,6 +40,14 @@ internal class WcEthAddNetworkUseCase @AssistedInject constructor(
|
|||
else -> WcNetworkDerivationState.Single
|
||||
}
|
||||
|
||||
private val addSwitchCommonDelegate = addSwitchCommonDelegateFactory.create(context)
|
||||
|
||||
override suspend fun invoke(): Either<HandleMethodError, WcAddNetworkUseCase.AddNetwork> {
|
||||
return addSwitchCommonDelegate
|
||||
.commonChecks(method.rawChain.chainId)
|
||||
.map { addedNetwork -> WcAddNetworkUseCase.AddNetwork(addedNetwork) }
|
||||
}
|
||||
|
||||
override suspend fun approve(): Either<WcRequestError, String> {
|
||||
return respondService.respond(rawSdkRequest, "")
|
||||
}
|
||||
|
|
@ -43,4 +60,31 @@ internal class WcEthAddNetworkUseCase @AssistedInject constructor(
|
|||
interface Factory {
|
||||
fun create(context: WcMethodUseCaseContext, method: WcEthMethod.AddEthereumChain): WcEthAddNetworkUseCase
|
||||
}
|
||||
}
|
||||
|
||||
internal class WcEthAddSwitchCommonDelegate @AssistedInject constructor(
|
||||
private val networksConverter: WcNetworksConverter,
|
||||
@Assisted val context: WcMethodUseCaseContext,
|
||||
) {
|
||||
|
||||
private val wallet: UserWallet get() = context.session.wallet
|
||||
|
||||
suspend fun commonChecks(hexChainId: String): Either<HandleMethodError, Network> {
|
||||
val caip2 = CAIP2.fromRaw("$ETH_NAMESPACE_KEY:${hexChainId.hexToInt()}")
|
||||
?: return HandleMethodError.UnknownError("Failed to parse CAIP2").left()
|
||||
val generalNetwork = networksConverter.createNetwork(caip2.raw, wallet)
|
||||
if (generalNetwork == null) {
|
||||
return HandleMethodError.TangemUnsupportedNetwork(caip2.raw).left()
|
||||
}
|
||||
val addedNetwork = networksConverter.mainOrAnyWalletNetworkForRequest(caip2.raw, wallet)
|
||||
if (addedNetwork == null) {
|
||||
return HandleMethodError.NotAddedNetwork(generalNetwork.name).left()
|
||||
}
|
||||
return addedNetwork.right()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(context: WcMethodUseCaseContext): WcEthAddSwitchCommonDelegate
|
||||
}
|
||||
}
|
||||
|
|
@ -6,17 +6,14 @@ import arrow.core.left
|
|||
import arrow.core.right
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.extensions.hexToInt
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.walletconnect.model.CAIP2
|
||||
import com.tangem.data.walletconnect.model.NamespaceKey
|
||||
import com.tangem.data.walletconnect.network.ethereum.WcEthNetwork.NamespaceConverter.Companion.ETH_NAMESPACE_KEY
|
||||
import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter
|
||||
import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Companion.fromJson
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
|
||||
import com.tangem.data.walletconnect.utils.WcNetworksConverter
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.walletconnect.model.*
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.domain.walletconnect.repository.WcSessionsManager
|
||||
|
|
@ -46,7 +43,7 @@ internal class WcEthNetwork(
|
|||
?: return HandleMethodError.UnknownSession.left()
|
||||
val wallet = session.wallet
|
||||
val chainId = request.chainId.orEmpty()
|
||||
val method: WcEthMethod = name.toMethod(request, wallet)
|
||||
val method: WcEthMethod = name.toMethod(request)
|
||||
.getOrElse { return error(it.message.orEmpty()) }
|
||||
?: return error("Failed to parse $name")
|
||||
suspend fun anyExistNetwork() = networksConverter.mainOrAnyWalletNetworkForRequest(chainId, wallet)
|
||||
|
|
@ -56,7 +53,9 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.SendTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTypedData -> method.account
|
||||
is WcEthMethod.AddEthereumChain ->
|
||||
is WcEthMethod.AddEthereumChain,
|
||||
is WcEthMethod.SwitchEthereumChain,
|
||||
->
|
||||
anyExistNetwork()
|
||||
?.let { network -> walletManagersFacade.getDefaultAddress(wallet.walletId, network).orEmpty() }
|
||||
.orEmpty()
|
||||
|
|
@ -67,7 +66,9 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.SendTransaction,
|
||||
is WcEthMethod.SignTransaction,
|
||||
-> networksConverter.findWalletNetworkForRequest(request, session, accountAddress)
|
||||
is WcEthMethod.AddEthereumChain -> anyExistNetwork()
|
||||
is WcEthMethod.AddEthereumChain,
|
||||
is WcEthMethod.SwitchEthereumChain,
|
||||
-> anyExistNetwork()
|
||||
} ?: return error("Failed to find walletNetwork for accountAddress $accountAddress")
|
||||
|
||||
val context = WcMethodUseCaseContext(
|
||||
|
|
@ -83,13 +84,11 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.SignTransaction -> factories.signTransaction.create(context, method)
|
||||
is WcEthMethod.SignTypedData -> factories.signTypedData.create(context, method)
|
||||
is WcEthMethod.AddEthereumChain -> factories.addNetwork.create(context, method)
|
||||
is WcEthMethod.SwitchEthereumChain -> factories.switchNetwork.create(context, method)
|
||||
}.right()
|
||||
}
|
||||
|
||||
private suspend fun WcEthMethodName.toMethod(
|
||||
request: WcSdkSessionRequest,
|
||||
wallet: UserWallet,
|
||||
): Either<Throwable, WcEthMethod?> {
|
||||
private fun WcEthMethodName.toMethod(request: WcSdkSessionRequest): Either<Throwable, WcEthMethod?> {
|
||||
val rawParams = request.request.params
|
||||
return when (this) {
|
||||
WcEthMethodName.EthSign,
|
||||
|
|
@ -111,16 +110,17 @@ internal class WcEthNetwork(
|
|||
}
|
||||
}
|
||||
?: return null.right()
|
||||
WcEthMethodName.AddEthereumChain -> moshi.fromJson<List<WcEthAddChain>>(rawParams)
|
||||
WcEthMethodName.AddEthereumChain,
|
||||
WcEthMethodName.SwitchEthereumChain,
|
||||
-> moshi.fromJson<List<WcEthAddChain>>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.firstOrNull()
|
||||
?.let {
|
||||
val caip2 = CAIP2.fromRaw("$ETH_NAMESPACE_KEY:${it.chainId.hexToInt()}")
|
||||
?: return null.right()
|
||||
val newNetwork = networksConverter
|
||||
.mainOrAnyWalletNetworkForRequest(caip2.raw, wallet)
|
||||
?: return null.right()
|
||||
WcEthMethod.AddEthereumChain(rawChain = it, network = newNetwork).right()
|
||||
if (this == WcEthMethodName.AddEthereumChain) {
|
||||
WcEthMethod.AddEthereumChain(rawChain = it).right()
|
||||
} else {
|
||||
WcEthMethod.SwitchEthereumChain(rawChain = it).right()
|
||||
}
|
||||
}
|
||||
?: null.right()
|
||||
}
|
||||
|
|
@ -170,5 +170,6 @@ internal class WcEthNetwork(
|
|||
val sendTransaction: WcEthSendTransactionUseCase.Factory,
|
||||
val signTransaction: WcEthSignTransactionUseCase.Factory,
|
||||
val addNetwork: WcEthAddNetworkUseCase.Factory,
|
||||
val switchNetwork: WcEthSwitchNetworkUseCase.Factory,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.data.walletconnect.network.ethereum
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.walletconnect.model.HandleMethodError
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.model.WcSession
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcNetworkDerivationState
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSwitchNetworkUseCase
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class WcEthSwitchNetworkUseCase @AssistedInject constructor(
|
||||
private val respondService: WcRespondService,
|
||||
@Assisted val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcEthMethod.SwitchEthereumChain,
|
||||
addSwitchCommonDelegateFactory: WcEthAddSwitchCommonDelegate.Factory,
|
||||
) : WcSwitchNetworkUseCase {
|
||||
|
||||
override val session: WcSession
|
||||
get() = context.session
|
||||
override val rawSdkRequest: WcSdkSessionRequest
|
||||
get() = context.rawSdkRequest
|
||||
override val network: Network
|
||||
get() = context.network
|
||||
override val derivationState: WcNetworkDerivationState = when {
|
||||
context.networkDerivationsCount > 1 -> WcNetworkDerivationState.Multiple(walletAddress = context.accountAddress)
|
||||
else -> WcNetworkDerivationState.Single
|
||||
}
|
||||
|
||||
private val addSwitchCommonDelegate = addSwitchCommonDelegateFactory.create(context)
|
||||
|
||||
override suspend fun invoke(): Either<HandleMethodError, WcSwitchNetworkUseCase.SwitchNetwork> {
|
||||
return addSwitchCommonDelegate
|
||||
.commonChecks(method.rawChain.chainId)
|
||||
.map { addedNetwork -> WcSwitchNetworkUseCase.SwitchNetwork(addedNetwork) }
|
||||
}
|
||||
|
||||
override fun reject() {
|
||||
respondService.rejectRequestNonBlock(rawSdkRequest)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(context: WcMethodUseCaseContext, method: WcEthMethod.SwitchEthereumChain): WcEthSwitchNetworkUseCase
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ internal class DefaultWcRequestService(
|
|||
Timber.tag(WC_TAG).i("handle request name $name")
|
||||
if (name is WcMethodName.Unsupported) {
|
||||
respondService.rejectRequestNonBlock(sr)
|
||||
if (name.raw.startsWith("wallet_")) return
|
||||
}
|
||||
_wcRequest.trySend(name to sr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ internal class WcNetworksConverter @Inject constructor(
|
|||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) {
|
||||
|
||||
fun createNetwork(chainId: String, wallet: UserWallet): Network? {
|
||||
return namespaceConverters
|
||||
.firstNotNullOfOrNull { it.toNetwork(chainId, wallet) }
|
||||
}
|
||||
|
||||
suspend fun findWalletNetworkForRequest(
|
||||
request: WcSdkSessionRequest,
|
||||
session: WcSession,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.domain.walletconnect.model
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
sealed interface WcEthMethod : WcMethod {
|
||||
|
||||
data class MessageSign(
|
||||
|
|
@ -28,6 +26,9 @@ sealed interface WcEthMethod : WcMethod {
|
|||
|
||||
data class AddEthereumChain(
|
||||
val rawChain: WcEthAddChain,
|
||||
val network: Network,
|
||||
) : WcEthMethod
|
||||
|
||||
data class SwitchEthereumChain(
|
||||
val rawChain: WcEthAddChain,
|
||||
) : WcEthMethod
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ enum class WcEthMethodName(override val raw: String) : WcMethodName {
|
|||
SignTransaction("eth_signTransaction"),
|
||||
SendTransaction("eth_sendTransaction"),
|
||||
AddEthereumChain("wallet_addEthereumChain"),
|
||||
SwitchEthereumChain("wallet_switchEthereumChain"),
|
||||
}
|
||||
|
||||
enum class WcSolanaMethodName(override val raw: String) : WcMethodName {
|
||||
|
|
|
|||
|
|
@ -69,4 +69,9 @@ sealed class HandleMethodError(
|
|||
data object UnknownSession : HandleMethodError(message = "WalletConnect session was disconnected")
|
||||
|
||||
data class UnknownError(override val message: String) : HandleMethodError(message)
|
||||
data class TangemUnsupportedNetwork(val unsupportedNetwork: String) :
|
||||
HandleMethodError("TangemUnsupportedNetwork $unsupportedNetwork")
|
||||
|
||||
data class NotAddedNetwork(val networkName: String) : HandleMethodError("NotAddedNetwork $networkName")
|
||||
data class RequiredNetwork(val networkName: String) : HandleMethodError("RequiredNetwork $networkName")
|
||||
}
|
||||
|
|
@ -1,12 +1,19 @@
|
|||
package com.tangem.domain.walletconnect.usecase.method
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.walletconnect.model.HandleMethodError
|
||||
import com.tangem.domain.walletconnect.model.WcRequestError
|
||||
|
||||
interface WcAddNetworkUseCase :
|
||||
WcMethodUseCase,
|
||||
WcMethodContext {
|
||||
|
||||
suspend operator fun invoke(): Either<HandleMethodError, AddNetwork>
|
||||
suspend fun approve(): Either<WcRequestError, String>
|
||||
fun reject()
|
||||
|
||||
data class AddNetwork(
|
||||
val network: Network,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.walletconnect.usecase.method
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.walletconnect.model.HandleMethodError
|
||||
|
||||
interface WcSwitchNetworkUseCase :
|
||||
WcMethodUseCase,
|
||||
WcMethodContext {
|
||||
|
||||
suspend operator fun invoke(): Either<HandleMethodError, SwitchNetwork>
|
||||
fun reject()
|
||||
|
||||
data class SwitchNetwork(
|
||||
val network: Network,
|
||||
)
|
||||
}
|
||||
|
|
@ -38,5 +38,23 @@ internal class AlertsComponent(
|
|||
|
||||
@Serializable
|
||||
data class WcDisconnected(override val onDismiss: () -> Unit) : AlertType()
|
||||
|
||||
@Serializable
|
||||
data class TangemUnsupportedNetwork(
|
||||
val network: String,
|
||||
override val onDismiss: () -> Unit,
|
||||
) : AlertType()
|
||||
|
||||
@Serializable
|
||||
data class RequiredAddNetwork(
|
||||
val network: String,
|
||||
override val onDismiss: () -> Unit,
|
||||
) : AlertType()
|
||||
|
||||
@Serializable
|
||||
data class RequiredReconnectWithNetwork(
|
||||
val network: String,
|
||||
override val onDismiss: () -> Unit,
|
||||
) : AlertType()
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,10 @@ import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
|
|||
import com.tangem.features.send.v2.api.FeeSelectorComponent
|
||||
import com.tangem.features.walletconnect.components.WcRoutingComponent
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponent
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponent.AlertType.*
|
||||
import com.tangem.features.walletconnect.connections.components.WcPairComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.chain.WcAddNetworkContainerComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.chain.WcSwitchNetworkComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.components.send.WcSendTransactionContainerComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.sign.WcSignTransactionContainerComponent
|
||||
|
|
@ -71,6 +73,10 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
|
|||
appComponentContext = childContext,
|
||||
params = WcTransactionModelParams(config.rawRequest),
|
||||
)
|
||||
is WcInnerRoute.SwitchNetwork -> WcSwitchNetworkComponent(
|
||||
appComponentContext = childContext,
|
||||
params = WcTransactionModelParams(config.rawRequest),
|
||||
)
|
||||
is WcInnerRoute.Send -> WcSendTransactionContainerComponent(
|
||||
appComponentContext = childContext,
|
||||
params = WcTransactionModelParams(config.rawRequest),
|
||||
|
|
@ -88,13 +94,31 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
|
|||
is WcInnerRoute.UnsupportedMethodAlert -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = AlertsComponent.AlertType.UnsupportedMethod { model.innerRouter.pop() },
|
||||
alertType = UnsupportedMethod { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
is WcInnerRoute.WcDappDisconnected -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = AlertsComponent.AlertType.WcDisconnected { model.innerRouter.pop() },
|
||||
alertType = WcDisconnected { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
is WcInnerRoute.TangemUnsupportedNetwork -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = TangemUnsupportedNetwork(config.networkName) { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
is WcInnerRoute.RequiredAddNetwork -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = RequiredAddNetwork(config.networkName) { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
is WcInnerRoute.RequiredReconnectWithNetwork -> AlertsComponent(
|
||||
childContext,
|
||||
AlertsComponent.Params(
|
||||
alertType = RequiredReconnectWithNetwork(config.networkName) { model.innerRouter.pop() },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ internal sealed interface WcInnerRoute : Route {
|
|||
@Serializable
|
||||
data class AddNetwork(override val rawRequest: WcSdkSessionRequest) : Method
|
||||
|
||||
@Serializable
|
||||
data class SwitchNetwork(override val rawRequest: WcSdkSessionRequest) : Method
|
||||
|
||||
@Serializable
|
||||
data class Pair(val request: WcPairRequest) : WcInnerRoute
|
||||
|
||||
|
|
@ -30,4 +33,13 @@ internal sealed interface WcInnerRoute : Route {
|
|||
|
||||
@Serializable
|
||||
data object WcDappDisconnected : WcInnerRoute
|
||||
|
||||
@Serializable
|
||||
data class TangemUnsupportedNetwork(val networkName: String) : WcInnerRoute
|
||||
|
||||
@Serializable
|
||||
data class RequiredAddNetwork(val networkName: String) : WcInnerRoute
|
||||
|
||||
@Serializable
|
||||
data class RequiredReconnectWithNetwork(val networkName: String) : WcInnerRoute
|
||||
}
|
||||
|
|
@ -51,6 +51,8 @@ internal class WcRoutingModel @Inject constructor(
|
|||
-> WcInnerRoute.SignMessage(rawRequest)
|
||||
WcEthMethodName.AddEthereumChain,
|
||||
-> WcInnerRoute.AddNetwork(rawRequest)
|
||||
WcEthMethodName.SwitchEthereumChain,
|
||||
-> WcInnerRoute.SwitchNetwork(rawRequest)
|
||||
WcEthMethodName.SignTransaction,
|
||||
WcEthMethodName.SendTransaction,
|
||||
WcSolanaMethodName.SignTransaction,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,11 @@ private fun ButtonsContainer(alert: AlertsComponent.AlertType, modifier: Modifie
|
|||
) {
|
||||
val buttonsModifier = Modifier.fillMaxWidth()
|
||||
when (alert) {
|
||||
is AlertsComponent.AlertType.UnsupportedMethod -> SecondaryButton(
|
||||
is AlertsComponent.AlertType.UnsupportedMethod,
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork,
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
|
||||
-> SecondaryButton(
|
||||
modifier = buttonsModifier,
|
||||
onClick = alert.onDismiss,
|
||||
text = stringResourceSafe(R.string.balance_hidden_got_it_button),
|
||||
|
|
@ -105,16 +109,28 @@ private fun ButtonsContainer(alert: AlertsComponent.AlertType, modifier: Modifie
|
|||
private fun AlertIcon(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
|
||||
val color = when (alert) {
|
||||
is AlertsComponent.AlertType.WcDisconnected -> TangemTheme.colors.icon.informative
|
||||
is AlertsComponent.AlertType.UnsupportedMethod -> TangemTheme.colors.icon.attention
|
||||
is AlertsComponent.AlertType.UnsupportedMethod,
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork,
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
|
||||
-> TangemTheme.colors.icon.attention
|
||||
}
|
||||
|
||||
@DrawableRes val drawableId = when (alert) {
|
||||
is AlertsComponent.AlertType.WcDisconnected -> R.drawable.ic_wallet_connect_24
|
||||
is AlertsComponent.AlertType.UnsupportedMethod -> R.drawable.img_attention_20
|
||||
is AlertsComponent.AlertType.UnsupportedMethod,
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork,
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
|
||||
-> R.drawable.img_attention_20
|
||||
}
|
||||
val iconTint = when (alert) {
|
||||
is AlertsComponent.AlertType.WcDisconnected -> color
|
||||
is AlertsComponent.AlertType.UnsupportedMethod -> Color.Unspecified
|
||||
is AlertsComponent.AlertType.UnsupportedMethod,
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork,
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
|
||||
-> Color.Unspecified
|
||||
}
|
||||
Box(
|
||||
modifier = modifier
|
||||
|
|
@ -138,6 +154,9 @@ private fun AlertContentTitle(alert: AlertsComponent.AlertType, modifier: Modifi
|
|||
@StringRes val titleRes: Int = when (alert) {
|
||||
is AlertsComponent.AlertType.WcDisconnected -> R.string.wc_alert_session_disconnected_title
|
||||
is AlertsComponent.AlertType.UnsupportedMethod -> R.string.wc_alert_unsupported_method_title
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork -> R.string.wc_alert_add_network_to_portfolio_title
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork -> R.string.wc_alert_network_not_connected_title
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork -> R.string.wc_alert_unsupported_network_title
|
||||
}
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
|
@ -157,6 +176,18 @@ private fun AlertContentDescription(alert: AlertsComponent.AlertType, modifier:
|
|||
is AlertsComponent.AlertType.UnsupportedMethod -> stringResourceSafe(
|
||||
R.string.wc_alert_unsupported_method_description,
|
||||
)
|
||||
is AlertsComponent.AlertType.RequiredAddNetwork -> stringResourceSafe(
|
||||
R.string.wc_alert_unsupported_method_description,
|
||||
alert.network,
|
||||
)
|
||||
is AlertsComponent.AlertType.RequiredReconnectWithNetwork -> stringResourceSafe(
|
||||
R.string.wc_alert_network_not_connected_description,
|
||||
alert.network,
|
||||
)
|
||||
is AlertsComponent.AlertType.TangemUnsupportedNetwork -> stringResourceSafe(
|
||||
R.string.wc_alert_unsupported_network_description,
|
||||
alert.network,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
|
@ -188,5 +219,8 @@ private class AlertTypesProvider : CollectionPreviewParameterProvider<AlertsComp
|
|||
collection = listOf(
|
||||
AlertsComponent.AlertType.WcDisconnected(onDismiss = {}),
|
||||
AlertsComponent.AlertType.UnsupportedMethod(onDismiss = {}),
|
||||
AlertsComponent.AlertType.TangemUnsupportedNetwork(network = "Solana", onDismiss = {}),
|
||||
AlertsComponent.AlertType.RequiredAddNetwork(network = "Solana", onDismiss = {}),
|
||||
AlertsComponent.AlertType.RequiredReconnectWithNetwork(network = "Solana", onDismiss = {}),
|
||||
),
|
||||
)
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.features.walletconnect.connections.routing.WcRoutingModel
|
|||
import com.tangem.features.walletconnect.transaction.model.WcAddNetworkModel
|
||||
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.model.WcSignTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.model.WcSwitchNetworkModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -57,6 +58,11 @@ internal interface WalletConnectModelModule {
|
|||
@ClassKey(WcAddNetworkModel::class)
|
||||
fun bindWcAddNetworkModel(model: WcAddNetworkModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WcSwitchNetworkModel::class)
|
||||
fun bindWcSwitchNetworkModel(model: WcSwitchNetworkModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WcSendTransactionModel::class)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.features.walletconnect.transaction.components.chain
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.model.WcSwitchNetworkModel
|
||||
|
||||
internal class WcSwitchNetworkComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: WcTransactionModelParams,
|
||||
) : AppComponentContext by appComponentContext, ComposableContentComponent {
|
||||
|
||||
private val model: WcSwitchNetworkModel = getOrCreateModel(params = params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,9 @@ internal object WcHandleMethodErrorConverter :
|
|||
is HandleMethodError.UnknownError,
|
||||
-> WcInnerRoute.UnsupportedMethodAlert
|
||||
HandleMethodError.UnknownSession -> WcInnerRoute.WcDappDisconnected
|
||||
|
||||
is HandleMethodError.NotAddedNetwork -> WcInnerRoute.RequiredAddNetwork(value.networkName)
|
||||
is HandleMethodError.RequiredNetwork -> WcInnerRoute.RequiredReconnectWithNetwork(value.networkName)
|
||||
is HandleMethodError.TangemUnsupportedNetwork -> WcInnerRoute.TangemUnsupportedNetwork(value.unsupportedNetwork)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,22 +57,27 @@ internal class WcAddNetworkModel @Inject constructor(
|
|||
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
|
||||
.getOrNull() ?: return@launch
|
||||
sendSignatureReceivedAnalytics(useCase)
|
||||
_uiState.emit(
|
||||
wcAddEthereumChainUMConverter.convert(
|
||||
WcAddEthereumChainUMConverter.Input(
|
||||
useCase = useCase,
|
||||
actions = WcTransactionActionsUM(
|
||||
onShowVerifiedAlert = ::showVerifiedAlert,
|
||||
onDismiss = { cancel(useCase) },
|
||||
onSign = { sign(useCase) },
|
||||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val either = useCase.invoke()
|
||||
either
|
||||
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
|
||||
.map { showUI() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUI() {
|
||||
_uiState.value = wcAddEthereumChainUMConverter.convert(
|
||||
WcAddEthereumChainUMConverter.Input(
|
||||
useCase = useCase,
|
||||
actions = WcTransactionActionsUM(
|
||||
onShowVerifiedAlert = ::showVerifiedAlert,
|
||||
onDismiss = { cancel(useCase) },
|
||||
onSign = { sign(useCase) },
|
||||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.features.walletconnect.transaction.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
|
||||
import com.tangem.domain.walletconnect.model.HandleMethodError
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSwitchNetworkUseCase
|
||||
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
|
||||
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class WcSwitchNetworkModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val useCaseFactory: WcRequestUseCaseFactory,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<WcTransactionModelParams>()
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
val useCase = useCaseFactory.createUseCase<WcSwitchNetworkUseCase>(params.rawRequest)
|
||||
.onLeft { showErrorDialog(it) }
|
||||
.getOrNull() ?: return@launch
|
||||
val either = useCase.invoke()
|
||||
useCase.reject()
|
||||
either
|
||||
.onLeft { showErrorDialog(it) }
|
||||
.map { showErrorDialog(HandleMethodError.RequiredNetwork(it.network.name)) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorDialog(error: HandleMethodError) {
|
||||
router.push(WcHandleMethodErrorConverter.convert(error))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue