From 63a718d7714a57f2d9bfaf3f40d8cf6bc6dc7399 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Jul 2025 22:47:25 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../DefaultTransactionRepository.kt | 32 ++++------- .../transaction/di/TransactionDataModule.kt | 4 +- .../ethereum/WcEthAddNetworkUseCase.kt | 3 +- .../ethereum/WcEthMessageSignUseCase.kt | 3 +- .../ethereum/WcEthSendTransactionUseCase.kt | 4 +- .../ethereum/WcEthSignTransactionUseCase.kt | 3 +- .../ethereum/WcEthSignTypedDataUseCase.kt | 3 +- .../solana/WcSolanaMessageSignUseCase.kt | 3 +- .../WcSolanaSignAllTransactionUseCase.kt | 3 +- .../solana/WcSolanaSignTransactionUseCase.kt | 3 +- .../respond/DefaultWcRespondService.kt | 14 ++++- .../walletconnect/respond/WcRespondService.kt | 3 +- .../walletconnect/sign/SignStateConverter.kt | 15 ++--- .../sign/WcSignUseCaseDelegate.kt | 9 ++- .../WcSignUseCaseDelegateTest.kt | 13 +++-- .../transaction/TransactionRepository.kt | 4 +- .../usecase/PrepareForSendUseCase.kt | 20 +++++-- .../usecase/SendTransactionUseCase.kt | 44 ++++++++------- domain/wallet-connect/build.gradle.kts | 3 + domain/wallet-connect/models/build.gradle.kts | 1 + .../walletconnect/model/WcRequestError.kt | 56 +++++++++++++++++++ .../walletconnect/error/ErrorsMapper.kt | 16 ++++++ .../usecase/method/WcAddNetworkUseCase.kt | 3 +- .../usecase/method/WcMethodBase.kt | 3 +- .../model/WcSendTransactionModel.kt | 6 +- 25 files changed, 186 insertions(+), 85 deletions(-) create mode 100644 domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcRequestError.kt create mode 100644 domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/error/ErrorsMapper.kt diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index 30d55b6061..9ae6b3d6b1 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -36,7 +36,7 @@ import java.math.BigInteger internal class DefaultTransactionRepository( private val walletManagersFacade: WalletManagersFacade, private val walletManagersStore: WalletManagersStore, - private val coroutineDispatcherProvider: CoroutineDispatcherProvider, + private val dispatchers: CoroutineDispatcherProvider, ) : TransactionRepository { override suspend fun createTransaction( @@ -47,7 +47,7 @@ internal class DefaultTransactionRepository( userWalletId: UserWalletId, network: Network, txExtras: TransactionExtras?, - ): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) { + ): TransactionData.Uncompiled = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val walletManager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, @@ -84,7 +84,7 @@ internal class DefaultTransactionRepository( userWalletId: UserWalletId, network: Network, nonce: BigInteger?, - ): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) { + ): TransactionData.Uncompiled = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val walletManager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, @@ -138,7 +138,7 @@ internal class DefaultTransactionRepository( spenderAddress: String, userWalletId: UserWalletId, network: Network, - ): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) { + ): TransactionData.Uncompiled = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val extras = createTransactionDataExtras( @@ -171,7 +171,7 @@ internal class DefaultTransactionRepository( destinationAddress: String, userWalletId: UserWalletId, network: Network, - ): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) { + ): TransactionData.Uncompiled = withContext(dispatchers.io) { val blockchain = network.toBlockchain() // For now transfer one nft asset at a time @@ -229,7 +229,7 @@ internal class DefaultTransactionRepository( destination: String, userWalletId: UserWalletId, network: Network, - ): Result = withContext(coroutineDispatcherProvider.io) { + ): Result = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val walletManager = walletManagersStore.getSyncOrNull( userWalletId = userWalletId, @@ -260,7 +260,7 @@ internal class DefaultTransactionRepository( signer: TransactionSigner, userWalletId: UserWalletId, network: Network, - ) = withContext(coroutineDispatcherProvider.io) { + ) = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val walletManager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, @@ -276,7 +276,7 @@ internal class DefaultTransactionRepository( userWalletId: UserWalletId, network: Network, sendMode: TransactionSender.MultipleTransactionSendMode, - ) = withContext(coroutineDispatcherProvider.io) { + ) = withContext(dispatchers.io) { val blockchain = network.toBlockchain() val walletManager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, @@ -366,13 +366,9 @@ internal class DefaultTransactionRepository( signer: TransactionSigner, userWalletId: UserWalletId, network: Network, - ): Result = withContext(coroutineDispatcherProvider.io) { + ) = withContext(dispatchers.io) { val preparer = getPreparer(network, userWalletId) - - when (val prepareForSend = preparer.prepareForSend(transactionData, signer)) { - is com.tangem.blockchain.extensions.Result.Failure -> Result.failure(prepareForSend.error) - is com.tangem.blockchain.extensions.Result.Success -> Result.success(prepareForSend.data) - } + preparer.prepareForSend(transactionData, signer) } override suspend fun prepareForSendMultiple( @@ -380,13 +376,9 @@ internal class DefaultTransactionRepository( signer: TransactionSigner, userWalletId: UserWalletId, network: Network, - ): Result> = withContext(coroutineDispatcherProvider.io) { + ) = withContext(dispatchers.io) { val preparer = getPreparer(network, userWalletId) - - when (val prepareForSend = preparer.prepareForSendMultiple(transactionData, signer)) { - is com.tangem.blockchain.extensions.Result.Failure -> Result.failure(prepareForSend.error) - is com.tangem.blockchain.extensions.Result.Success -> Result.success(prepareForSend.data) - } + preparer.prepareForSendMultiple(transactionData, signer) } private suspend fun getPreparer(network: Network, userWalletId: UserWalletId): TransactionPreparer { diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt index d5fffe95ed..6184d3fa48 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt @@ -24,12 +24,12 @@ internal object TransactionDataModule { fun providesTransactionRepository( walletManagersFacade: WalletManagersFacade, walletManagersStore: WalletManagersStore, - coroutineDispatcherProvider: CoroutineDispatcherProvider, + dispatchers: CoroutineDispatcherProvider, ): TransactionRepository { return DefaultTransactionRepository( walletManagersFacade = walletManagersFacade, walletManagersStore = walletManagersStore, - coroutineDispatcherProvider = coroutineDispatcherProvider, + dispatchers = dispatchers, ) } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthAddNetworkUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthAddNetworkUseCase.kt index d2558e2958..ed0e0bd7d9 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthAddNetworkUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthAddNetworkUseCase.kt @@ -5,6 +5,7 @@ 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.WcEthMethod +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase @@ -27,7 +28,7 @@ internal class WcEthAddNetworkUseCase @AssistedInject constructor( override val walletAddress: String get() = context.accountAddress - override suspend fun approve(): Either { + override suspend fun approve(): Either { return respondService.respond(rawSdkRequest, "") } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthMessageSignUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthMessageSignUseCase.kt index cdff1f2b9e..36d3087190 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthMessageSignUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthMessageSignUseCase.kt @@ -19,6 +19,7 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate import com.tangem.domain.transaction.usecase.SignUseCase +import com.tangem.domain.walletconnect.error.parseTangemSdkError import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase import com.tangem.domain.walletconnect.usecase.method.WcSignState @@ -59,7 +60,7 @@ internal class WcEthMessageSignUseCase @AssistedInject constructor( ?: return val signedHash = signUseCase(hashToSign, userWallet, network) - .onLeft { emit(state.toResult(it.left())) } + .onLeft { emit(state.toResult(parseTangemSdkError(it).left())) } .getOrNull() ?: return val respond = prepareToSendMessageData(signedHash, hashToSign, walletManager) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt index 4b80dbfbd4..e8de784b36 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSendTransactionUseCase.kt @@ -17,6 +17,7 @@ import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.tokens.model.Amount import com.tangem.domain.transaction.usecase.SendTransactionUseCase +import com.tangem.domain.walletconnect.error.parseSendError import com.tangem.domain.walletconnect.model.WcApprovedAmount import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.usecase.method.* @@ -80,8 +81,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor( override suspend fun SignCollector.onSign(state: WcSignState) { val hash = sendTransaction(state.signModel, wallet, network) .onLeft { error -> - val sendError = IllegalArgumentException(error.toString()) // todo(wc) use domain error - emit(state.toResult(sendError.left())) + emit(state.toResult(parseSendError(error).left())) } .getOrNull() ?: return val respondResult = respondService.respond(rawSdkRequest, hash.formatHex()) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt index b86988cbae..bb9de17e6e 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTransactionUseCase.kt @@ -17,6 +17,7 @@ import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate import com.tangem.domain.tokens.model.Amount import com.tangem.domain.transaction.usecase.PrepareForSendUseCase +import com.tangem.domain.walletconnect.error.parseSendError import com.tangem.domain.walletconnect.model.WcApprovedAmount import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.usecase.method.* @@ -82,7 +83,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor( val hash = prepareForSend(state.signModel, wallet, network) .map { it.toHexString().formatHex() } .onLeft { error -> - emit(state.toResult(error.left())) + emit(state.toResult(parseSendError(error).left())) } .getOrNull() ?: return diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTypedDataUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTypedDataUseCase.kt index 8e961267e1..c857512c54 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTypedDataUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthSignTypedDataUseCase.kt @@ -11,6 +11,7 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate import com.tangem.domain.transaction.usecase.SignUseCase +import com.tangem.domain.walletconnect.error.parseTangemSdkError import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase import com.tangem.domain.walletconnect.usecase.method.WcSignState @@ -51,7 +52,7 @@ internal class WcEthSignTypedDataUseCase @AssistedInject constructor( ?: return val signedHash = signUseCase(hashToSign, userWallet, network) - .onLeft { emit(state.toResult(it.left())) } + .onLeft { emit(state.toResult(parseTangemSdkError(it).left())) } .getOrNull() ?: return val respond = prepareToSendMessageData(signedHash, hashToSign, walletManager) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaMessageSignUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaMessageSignUseCase.kt index e82c5601ea..b8adc0c608 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaMessageSignUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaMessageSignUseCase.kt @@ -12,6 +12,7 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.transaction.usecase.SignUseCase +import com.tangem.domain.walletconnect.error.parseTangemSdkError import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase import com.tangem.domain.walletconnect.usecase.method.WcSignState @@ -40,7 +41,7 @@ internal class WcSolanaMessageSignUseCase @AssistedInject constructor( val userWallet = session.wallet val signedHash = signUseCase(hashToSign, userWallet, network) - .onLeft { emit(state.toResult(it.left())) } + .onLeft { emit(state.toResult(parseTangemSdkError(it).left())) } .getOrNull() ?: return val respond = "{ signature: \"${signedHash.encodeBase58()}\" }" diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignAllTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignAllTransactionUseCase.kt index b60d6ff124..8e91c2c8dd 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignAllTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignAllTransactionUseCase.kt @@ -11,6 +11,7 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult 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.error.parseSendError import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck import com.tangem.domain.walletconnect.usecase.method.WcListTransactionUseCase @@ -47,7 +48,7 @@ internal class WcSolanaSignAllTransactionUseCase @AssistedInject constructor( override suspend fun SignCollector>.onSign(state: WcSignState>) { val hash = prepareForSend.invoke(transactionData = state.signModel, userWallet = wallet, network = network) .onLeft { error -> - emit(state.toResult(error.left())) + emit(state.toResult(parseSendError(error).left())) } .getOrNull() ?: return diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignTransactionUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignTransactionUseCase.kt index 13e0fb026a..282125a721 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignTransactionUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaSignTransactionUseCase.kt @@ -11,6 +11,7 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult 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.error.parseSendError import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck import com.tangem.domain.walletconnect.usecase.method.WcSignState @@ -45,7 +46,7 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor( override suspend fun SignCollector.onSign(state: WcSignState) { val hash = prepareForSend.invoke(transactionData = state.signModel, userWallet = wallet, network = network) .onLeft { error -> - emit(state.toResult(error.left())) + emit(state.toResult(parseSendError(error).left())) } .getOrNull() ?: return diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt index e87d12d41f..8393816677 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt @@ -6,6 +6,7 @@ import arrow.core.right import com.reown.walletkit.client.Wallet import com.reown.walletkit.client.WalletKit import com.tangem.data.walletconnect.utils.WC_TAG +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import kotlinx.coroutines.suspendCancellableCoroutine import timber.log.Timber @@ -13,7 +14,7 @@ import kotlin.coroutines.resume internal class DefaultWcRespondService : WcRespondService { - override suspend fun respond(request: WcSdkSessionRequest, response: String): Either = + override suspend fun respond(request: WcSdkSessionRequest, response: String): Either = suspendCancellableCoroutine { continuation -> WalletKit.respondSessionRequest( params = Wallet.Params.SessionRequestResponse( @@ -26,12 +27,19 @@ internal class DefaultWcRespondService : WcRespondService { onSuccess = { if (continuation.isCompleted) return@respondSessionRequest Timber.tag(WC_TAG).i("Successful respond for request $request") - continuation.resume(Unit.right()) + val result = when (val response = it.jsonRpcResponse) { + is Wallet.Model.JsonRpcResponse.JsonRpcError -> WcRequestError.WcRespondError( + code = response.code, + message = response.message, + ).left() + is Wallet.Model.JsonRpcResponse.JsonRpcResult -> response.result.right() + } + continuation.resume(result) }, onError = { if (continuation.isCompleted) return@respondSessionRequest Timber.tag(WC_TAG).e(it.throwable, "Failed respond for request $request") - continuation.resume(it.throwable.left()) + continuation.resume(WcRequestError.UnknownError(it.throwable).left()) }, ) } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/WcRespondService.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/WcRespondService.kt index be259755f2..47fe01e948 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/WcRespondService.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/WcRespondService.kt @@ -1,9 +1,10 @@ package com.tangem.data.walletconnect.respond import arrow.core.Either +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest interface WcRespondService { - suspend fun respond(request: WcSdkSessionRequest, response: String): Either + suspend fun respond(request: WcSdkSessionRequest, response: String): Either fun rejectRequestNonBlock(request: WcSdkSessionRequest, message: String = "") } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/SignStateConverter.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/SignStateConverter.kt index 138f17d2c0..5c9e430c26 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/SignStateConverter.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/SignStateConverter.kt @@ -1,16 +1,12 @@ package com.tangem.data.walletconnect.sign import arrow.core.Either +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep object SignStateConverter { - internal fun preSign(signModel: M) = WcSignState(signModel, WcSignStep.PreSign) - internal fun signing(signModel: M) = WcSignState(signModel, WcSignStep.Signing) - internal fun result(result: Either, signModel: M) = - WcSignState(signModel, WcSignStep.Result(result)) - internal fun WcSignState.toPreSign(signModel: M = this.signModel) = copy( signModel = signModel, domainStep = WcSignStep.PreSign, @@ -21,8 +17,9 @@ object SignStateConverter { signModel = signModel, ) - internal fun WcSignState.toResult(result: Either, signModel: M = this.signModel) = copy( - domainStep = WcSignStep.Result(result), - signModel = signModel, - ) + internal fun WcSignState.toResult(result: Either, signModel: M = this.signModel) = + copy( + domainStep = WcSignStep.Result(result), + signModel = signModel, + ) } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt index 68bd9a22b5..2dea8ba415 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sign/WcSignUseCaseDelegate.kt @@ -6,6 +6,8 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toPreSign import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.SignStateConverter.toSigning import com.tangem.domain.walletconnect.WcAnalyticEvents +import com.tangem.domain.walletconnect.model.WcRequestError +import com.tangem.domain.walletconnect.model.WcRequestError.Companion.code import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep import kotlinx.coroutines.Job @@ -62,18 +64,19 @@ internal class WcSignUseCaseDelegate( fun signFlow() = flow { onSign(state.updateAndGet { it.toSigning() }) } .onEach { newState -> state.update { newState } } .catch { exception -> - val errorResult = state.value.toResult(exception.left()) + val errorResult = state.value + .toResult(WcRequestError.UnknownError(exception).left()) state.update { errorResult } } .onEach { state -> val step = state.domainStep as? WcSignStep.Result ?: return@onEach val event = step.result.fold( - ifLeft = { + ifLeft = { error -> WcAnalyticEvents.SignatureRequestFailed( session = context.session, rawRequest = context.rawSdkRequest, network = context.network, - it.message.orEmpty(), + errorCode = error.code() ?: error::class.simpleName.orEmpty(), ) }, ifRight = { diff --git a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/WcSignUseCaseDelegateTest.kt b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/WcSignUseCaseDelegateTest.kt index 4251b79b72..c78cc22391 100644 --- a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/WcSignUseCaseDelegateTest.kt +++ b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/WcSignUseCaseDelegateTest.kt @@ -10,6 +10,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.data.walletconnect.sign.* import com.tangem.data.walletconnect.sign.SignStateConverter.toResult import com.tangem.data.walletconnect.sign.SignStateConverter.toSigning +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession @@ -32,6 +33,7 @@ internal class WcSignUseCaseDelegateTest { object : FinalActionCollector {} private val initSignModel = TestSignModel() private val analytics: AnalyticsEventHandler = mockk(relaxed = true) + private val simpleResult = "hex".right() private val rawRequestMock = WcSdkSessionRequest( topic = "", chainId = "", @@ -64,14 +66,14 @@ internal class WcSignUseCaseDelegateTest { private val initState = WcSignState(initSignModel, WcSignStep.PreSign) private val signing = initState.toSigning() - private val result = signing.toResult(Unit.right()) - private val testException = RuntimeException("test") + private val result = signing.toResult(simpleResult) + private val testException = WcRequestError.UnknownError(RuntimeException("test")) private val successSign: suspend FlowCollector>.( currentState: WcSignState, ) -> Unit = { state -> delay(2) - emit(state.toResult(Unit.right())) + emit(state.toResult(simpleResult)) } private val failedSign: suspend FlowCollector>.( @@ -161,8 +163,9 @@ internal class WcSignUseCaseDelegateTest { @Test fun `failed sign and catch unknown exception`() = runTest { - val exception = RuntimeException("asd") - val expectedErrorState = signing.toResult(exception.left()) + val exception = RuntimeException("test") + val testException = WcRequestError.UnknownError(exception) + val expectedErrorState = signing.toResult(testException.left()) finalActionCollector = object : FinalActionCollector { override suspend fun SignCollector.onSign(state: WcSignState) { delay(2) diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt index 52a4d262b4..96263d3397 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt @@ -101,12 +101,12 @@ interface TransactionRepository { signer: TransactionSigner, userWalletId: UserWalletId, network: Network, - ): Result + ): com.tangem.blockchain.extensions.Result suspend fun prepareForSendMultiple( transactionData: List, signer: TransactionSigner, userWalletId: UserWalletId, network: Network, - ): Result> + ): com.tangem.blockchain.extensions.Result> } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt index dab4a7f379..3c5c19ec91 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt @@ -5,11 +5,13 @@ import arrow.core.left import arrow.core.right import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionSigner +import com.tangem.blockchain.extensions.Result import com.tangem.domain.card.models.TwinKey import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.models.network.Network import com.tangem.domain.transaction.TransactionRepository +import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.requireColdWallet @@ -21,30 +23,36 @@ class PrepareForSendUseCase( transactionData: TransactionData, userWallet: UserWallet, network: Network, - ): Either { + ): Either { val signer = createSigner(userWallet) - return transactionRepository.prepareForSend( + val result = transactionRepository.prepareForSend( transactionData = transactionData, userWalletId = userWallet.walletId, network = network, signer = signer, ) - .fold(onSuccess = { it.right() }, onFailure = { it.left() }) + return when (result) { + is Result.Failure -> SendTransactionUseCase.handleError(result).left() + is Result.Success -> result.data.right() + } } suspend operator fun invoke( transactionData: List, userWallet: UserWallet, network: Network, - ): Either> { + ): Either> { val signer = createSigner(userWallet) - return transactionRepository.prepareForSendMultiple( + val result = transactionRepository.prepareForSendMultiple( transactionData = transactionData, userWalletId = userWallet.walletId, network = network, signer = signer, ) - .fold(onSuccess = { it.right() }, onFailure = { it.left() }) + return when (result) { + is Result.Failure -> SendTransactionUseCase.handleError(result).left() + is Result.Success -> result.data.right() + } } private fun createSigner(userWallet: UserWallet): TransactionSigner { diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt index 9b8a7e80c2..fb901ec9f1 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt @@ -39,7 +39,7 @@ class SendTransactionUseCase( userWallet: UserWallet, network: Network, sendMode: TransactionSender.MultipleTransactionSendMode, - ): Either> { + ): Either> { userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY] val card = userWallet.scanResponse.card @@ -101,7 +101,7 @@ class SendTransactionUseCase( txData: TransactionData, userWallet: UserWallet, network: Network, - ): Either { + ): Either { return invoke(listOf(txData), userWallet, network, TransactionSender.MultipleTransactionSendMode.DEFAULT) .map { it.first() } } @@ -127,27 +127,29 @@ class SendTransactionUseCase( } } - private fun handleError(result: Result.Failure): SendTransactionError { - if (ResultChecker.isNetworkError(result)) { - return SendTransactionError.NetworkError( - code = result.error.message, - message = result.error.customMessage, - ) - } - val error = result.error as? BlockchainSdkError ?: return SendTransactionError.UnknownError() - return when (error) { - is BlockchainSdkError.WrappedTangemError -> parseWrappedError(error) - is BlockchainSdkError.CreateAccountUnderfunded -> { - val minAmount = error.minReserve - val minValue = minAmount.value?.format { simple(minAmount.decimals) }.orEmpty() - SendTransactionError.CreateAccountUnderfunded(minValue) - } - else -> { - SendTransactionError.BlockchainSdkError( - code = error.code, - message = error.customMessage, + companion object { + internal fun handleError(result: Result.Failure): SendTransactionError { + if (ResultChecker.isNetworkError(result)) { + return SendTransactionError.NetworkError( + code = result.error.message, + message = result.error.customMessage, ) } + val error = result.error as? BlockchainSdkError ?: return SendTransactionError.UnknownError() + return when (error) { + is BlockchainSdkError.WrappedTangemError -> parseWrappedError(error) + is BlockchainSdkError.CreateAccountUnderfunded -> { + val minAmount = error.minReserve + val minValue = minAmount.value?.format { simple(minAmount.decimals) }.orEmpty() + SendTransactionError.CreateAccountUnderfunded(minValue) + } + else -> { + SendTransactionError.BlockchainSdkError( + code = error.code, + message = error.customMessage, + ) + } + } } } } \ No newline at end of file diff --git a/domain/wallet-connect/build.gradle.kts b/domain/wallet-connect/build.gradle.kts index f24225c89c..6150ebfe15 100644 --- a/domain/wallet-connect/build.gradle.kts +++ b/domain/wallet-connect/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.walletConnect.models) + implementation(projects.domain.transaction) + implementation(projects.domain.transaction.models) /* Project - Core */ implementation(projects.core.analytics) @@ -25,4 +27,5 @@ dependencies { /* Tangem libraries */ implementation(tangemDeps.blockchain) + implementation(tangemDeps.card.core) } \ No newline at end of file diff --git a/domain/wallet-connect/models/build.gradle.kts b/domain/wallet-connect/models/build.gradle.kts index ddd2d3024a..0e4052be5f 100644 --- a/domain/wallet-connect/models/build.gradle.kts +++ b/domain/wallet-connect/models/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { implementation(projects.domain.wallets.models) implementation(projects.domain.tokens.models) implementation(projects.domain.blockaid.models) + implementation(projects.domain.transaction.models) /* Other */ implementation(deps.moshi) diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcRequestError.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcRequestError.kt new file mode 100644 index 0000000000..55a61db209 --- /dev/null +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcRequestError.kt @@ -0,0 +1,56 @@ +package com.tangem.domain.walletconnect.model + +import com.tangem.domain.transaction.error.SendTransactionError + +sealed class WcRequestError { + + data class WrappedSendError( + val sendTransactionError: SendTransactionError, + ) : WcRequestError() + + data class WcRespondError( + val code: Int, + val message: String, + ) : WcRequestError() + + data class UnknownError(val ex: Throwable? = null) : WcRequestError() + + companion object { + + fun WcRequestError.message(): String? = when (this) { + is UnknownError -> ex?.message + is WcRespondError -> message + is WrappedSendError -> sendTransactionError.message() + } + + fun WcRequestError.code(): String? = when (this) { + is UnknownError -> null + is WcRespondError -> this.code.toString() + is WrappedSendError -> sendTransactionError.code() + } + + private fun SendTransactionError.code(): String? = when (this) { + is SendTransactionError.BlockchainSdkError -> code.toString() + is SendTransactionError.NetworkError -> code + is SendTransactionError.TangemSdkError -> code.toString() + is SendTransactionError.DataError, + SendTransactionError.DemoCardError, + is SendTransactionError.UnknownError, + SendTransactionError.UserCancelledError, + is SendTransactionError.CreateAccountUnderfunded, + -> null + } + + private fun SendTransactionError.message(): String? = when (this) { + is SendTransactionError.BlockchainSdkError -> message + is SendTransactionError.NetworkError -> message + is SendTransactionError.DataError -> message + is SendTransactionError.UnknownError -> ex?.message + is SendTransactionError.TangemSdkError, + SendTransactionError.DemoCardError, + SendTransactionError.UserCancelledError, + is SendTransactionError.CreateAccountUnderfunded, + -> null + } + } +} \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/error/ErrorsMapper.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/error/ErrorsMapper.kt new file mode 100644 index 0000000000..db38ce80aa --- /dev/null +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/error/ErrorsMapper.kt @@ -0,0 +1,16 @@ +package com.tangem.domain.walletconnect.error + +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.common.core.TangemError +import com.tangem.domain.transaction.error.SendTransactionError +import com.tangem.domain.transaction.error.parseWrappedError +import com.tangem.domain.walletconnect.model.WcRequestError + +fun parseSendError(error: SendTransactionError): WcRequestError.WrappedSendError { + return WcRequestError.WrappedSendError(error) +} + +fun parseTangemSdkError(error: TangemError): WcRequestError.WrappedSendError { + val sendError = parseWrappedError(BlockchainSdkError.WrappedTangemError(error)) + return parseSendError(sendError) +} \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcAddNetworkUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcAddNetworkUseCase.kt index b7794de457..787bea6e93 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcAddNetworkUseCase.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcAddNetworkUseCase.kt @@ -1,11 +1,12 @@ package com.tangem.domain.walletconnect.usecase.method import arrow.core.Either +import com.tangem.domain.walletconnect.model.WcRequestError interface WcAddNetworkUseCase : WcMethodUseCase, WcMethodContext { - suspend fun approve(): Either + suspend fun approve(): Either fun reject() } \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcMethodBase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcMethodBase.kt index 5f95849a01..77f5081a18 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcMethodBase.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcMethodBase.kt @@ -3,6 +3,7 @@ package com.tangem.domain.walletconnect.usecase.method import arrow.core.Either import com.tangem.domain.models.network.Network import com.tangem.domain.walletconnect.model.WcMethod +import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.wallets.models.UserWallet @@ -38,5 +39,5 @@ data class WcSignState( sealed interface WcSignStep { data object PreSign : WcSignStep data object Signing : WcSignStep - data class Result(val result: Either) : WcSignStep + data class Result(val result: Either) : WcSignStep } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index db845051ad..ddcada687e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -22,6 +22,8 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.walletconnect.WcRequestUseCaseFactory +import com.tangem.domain.walletconnect.model.WcRequestError +import com.tangem.domain.walletconnect.model.WcRequestError.Companion.message import com.tangem.domain.walletconnect.usecase.method.* import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback @@ -252,10 +254,10 @@ internal class WcSendTransactionModel @Inject constructor( return false } - private fun handleSigningError(result: Either, useCase: WcSignUseCase<*>) { + private fun handleSigningError(result: Either, useCase: WcSignUseCase<*>) { if (result.isLeft()) { val error = WcTransactionRoutes.Alert.Type.UnknownError( - errorMessage = result.leftOrNull()?.message, + errorMessage = result.leftOrNull()?.message(), onDismiss = { cancel(useCase) }, ) stackNavigation.pushNew(WcTransactionRoutes.Alert(error))