Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-28 00:23:50 +07:00
parent 80d4eeaed9
commit e2bdc12ed8
24 changed files with 696 additions and 169 deletions

View file

@ -0,0 +1,14 @@
package com.tangem.domain.walletconnect.model
import com.tangem.domain.tokens.model.Network
sealed interface WcNetwork {
val name: String
data class Supported(val network: Network) : WcNetwork {
override val name: String get() = network.name
}
data class Unknown(override val name: String) : WcNetwork
}

View file

@ -0,0 +1,15 @@
package com.tangem.domain.walletconnect.model
sealed class WcPairError(override val message: String) : Exception(message) {
data object UnsupportedDApp : WcPairError("UnsupportedDApp")
data class UnsupportedNetworks(
val chains: Set<WcNetwork.Unknown>,
) : WcPairError("ApprovalErrorMissingNetworks")
data class ExternalApprovalError(
override val message: String,
) : WcPairError("ExternalApprovalError")
data class Unknown(override val message: String) : WcPairError(message)
}

View file

@ -0,0 +1,8 @@
package com.tangem.domain.walletconnect.model
import com.tangem.domain.wallets.models.UserWalletId
data class WcSessionApprove(
val walletId: UserWalletId,
val network: List<WcNetwork.Supported>,
)

View file

@ -1,15 +1,19 @@
package com.tangem.domain.walletconnect.model
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionProposal
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
import com.tangem.domain.wallets.models.UserWalletId
data class WcSessionProposal(
val dAppInfo: WcSdkSessionProposal,
val dAppMetaData: WcAppMetaData,
val proposalNetwork: Map<UserWalletId, ProposalNetwork>,
val securityStatus: Any,
) {
// todo(wc) should map for all user wallets?
val wallet: UserWallet,
val missingChains: List<Network>,
val availableChains: List<Network>,
val notAddedChains: List<Network>,
)
data class ProposalNetwork(
val walletId: UserWalletId,
val missingRequired: Set<WcNetwork.Supported>,
val required: Set<WcNetwork.Supported>,
val available: Set<WcNetwork.Supported>,
val notAdded: Set<WcNetwork.Supported>,
)
}

View file

@ -0,0 +1,15 @@
package com.tangem.domain.walletconnect.model.sdkcopy
/**
* copy of [com.reown.android.Core.Model.AppMetaData]
*/
data class WcAppMetaData(
val name: String,
val description: String,
val url: String,
val icons: List<String>,
val redirect: String?,
val appLink: String? = null,
val linkMode: Boolean = false,
val verifyUrl: String? = null,
)

View file

@ -1,18 +0,0 @@
package com.tangem.domain.walletconnect.request
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.WcRequest
interface WcRequestHandler<M : WcMethod> {
fun canHandle(methodName: String): Boolean
fun deserialize(methodName: String, params: String): M?
fun handle(wcRequest: WcRequest<M>)
companion object {
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> fromJson(params: String, moshi: Moshi): T? =
runCatching { moshi.adapter<T>().fromJsonValue(params) }.getOrNull()
}
}

View file

@ -3,5 +3,5 @@ package com.tangem.domain.walletconnect.usecase
import kotlinx.coroutines.flow.Flow
interface WcUseCasesFlowProvider {
val flow: Flow<WcUseCase>
val useCases: Flow<WcUseCase>
}

View file

@ -1,53 +0,0 @@
package com.tangem.domain.walletconnect.usecase.ethereum
import com.squareup.moshi.Moshi
import com.tangem.domain.walletconnect.model.WcRequest
import com.tangem.domain.walletconnect.request.WcRequestHandler
import com.tangem.domain.walletconnect.respond.WcRespondService
import com.tangem.domain.walletconnect.usecase.WcUseCase
import com.tangem.domain.walletconnect.usecase.WcUseCasesFlowProvider
import com.tangem.domain.walletconnect.usecase.ethereum.WcEthMethod.SignMessage
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
@Suppress("UnusedPrivateClass") // todo(wc) remove later
private class WcEthChain(
val moshi: Moshi,
private val respondService: WcRespondService,
) : WcRequestHandler<WcEthMethod>, WcUseCasesFlowProvider {
private val _flow: Channel<WcUseCase> = Channel(Channel.BUFFERED)
override val flow = _flow.receiveAsFlow()
override fun canHandle(methodName: String): Boolean {
return Name.entries.find { it.raw == methodName } != null
}
override fun deserialize(methodName: String, params: String): WcEthMethod? {
val name = Name.entries.find { it.raw == methodName } ?: return null
return when (name) {
Name.Sign -> TODO()
Name.PersonalSign -> WcRequestHandler.fromJson<SignMessage>(params, moshi)
Name.SignTypeData -> TODO()
Name.SignTypeDataV4 -> TODO()
Name.SignTransaction -> TODO()
Name.SendTransaction -> TODO()
}
}
override fun handle(wcRequest: WcRequest<WcEthMethod>) {
val useCase = when (wcRequest.method) {
is SignMessage -> EthPersonalSignUseCase(wcRequest as WcRequest<SignMessage>, respondService)
}
_flow.trySend(useCase)
}
enum class Name(val raw: String) {
Sign("eth_sign"),
PersonalSign("personal_sign"),
SignTypeData("eth_signTypedData"),
SignTypeDataV4("eth_signTypedData_v4"),
SignTransaction("eth_signTransaction"),
SendTransaction("eth_sendTransaction"),
}
}

View file

@ -1,24 +1,23 @@
package com.tangem.domain.walletconnect.usecase.pair
import arrow.core.Either
import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.model.WcSessionProposal
sealed interface WcPairState {
data object Loading : WcPairState
data class Error(val throwable: Throwable) : WcPairState
data class Proposal(
val dAppSession: WcSessionProposal,
val securityStatus: Any,
) : WcPairState
data class Error(val error: WcPairError) : WcPairState
data class Proposal(val dAppSession: WcSessionProposal) : WcPairState
sealed interface Approving : WcPairState {
val session: WcSessionProposal
val session: WcSessionApprove
data class Loading(override val session: WcSessionProposal) : Approving
data class Loading(override val session: WcSessionApprove) : Approving
data class Result(
override val session: WcSessionProposal,
val result: Either<Throwable, WcSession>,
override val session: WcSessionApprove,
val result: Either<WcPairError, WcSession>,
) : Approving
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.domain.walletconnect.usecase.pair
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.coroutines.flow.Flow
@ -8,12 +8,7 @@ interface WcPairUseCase {
fun pairFlow(uri: String, source: Source, selectedWallet: UserWallet): Flow<WcPairState>
// todo(wc) should map for all user wallets without this action in domain layer?
fun onWalletSelect(selectedWallet: UserWallet)
// todo(wc) accounts in future?
fun onAccountSelect(account: Any)
fun approve(sessionForApprove: WcSessionProposal)
fun approve(sessionForApprove: WcSessionApprove)
fun reject()
enum class Source { QR, DEEPLINK, ETC }