Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-10 22:47:25 +07:00
parent 67457f0746
commit 63a718d771
25 changed files with 186 additions and 85 deletions

View file

@ -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<Unit> = withContext(coroutineDispatcherProvider.io) {
): Result<Unit> = 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<ByteArray> = 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<List<ByteArray>> = 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 {

View file

@ -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,
)
}

View file

@ -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<Throwable, Unit> {
override suspend fun approve(): Either<WcRequestError, String> {
return respondService.respond(rawSdkRequest, "")
}

View file

@ -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)

View file

@ -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<TransactionData>.onSign(state: WcSignState<TransactionData>) {
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())

View file

@ -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

View file

@ -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)

View file

@ -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()}\" }"

View file

@ -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<List<TransactionData>>.onSign(state: WcSignState<List<TransactionData>>) {
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

View file

@ -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<TransactionData>.onSign(state: WcSignState<TransactionData>) {
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

View file

@ -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<Throwable, Unit> =
override suspend fun respond(request: WcSdkSessionRequest, response: String): Either<WcRequestError, String> =
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())
},
)
}

View file

@ -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<Throwable, Unit>
suspend fun respond(request: WcSdkSessionRequest, response: String): Either<WcRequestError, String>
fun rejectRequestNonBlock(request: WcSdkSessionRequest, message: String = "")
}

View file

@ -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 <M> preSign(signModel: M) = WcSignState(signModel, WcSignStep.PreSign)
internal fun <M> signing(signModel: M) = WcSignState(signModel, WcSignStep.Signing)
internal fun <M> result(result: Either<Throwable, Unit>, signModel: M) =
WcSignState(signModel, WcSignStep.Result(result))
internal fun <M> WcSignState<M>.toPreSign(signModel: M = this.signModel) = copy(
signModel = signModel,
domainStep = WcSignStep.PreSign,
@ -21,8 +17,9 @@ object SignStateConverter {
signModel = signModel,
)
internal fun <M> WcSignState<M>.toResult(result: Either<Throwable, Unit>, signModel: M = this.signModel) = copy(
domainStep = WcSignStep.Result(result),
signModel = signModel,
)
internal fun <M> WcSignState<M>.toResult(result: Either<WcRequestError, String>, signModel: M = this.signModel) =
copy(
domainStep = WcSignStep.Result(result),
signModel = signModel,
)
}

View file

@ -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<MiddleAction, SignModel>(
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 = {

View file

@ -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<TestSignModel> {}
private val initSignModel = TestSignModel()
private val analytics: AnalyticsEventHandler = mockk<AnalyticsEventHandler>(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<WcSignState<TestSignModel>>.(
currentState: WcSignState<TestSignModel>,
) -> Unit = { state ->
delay(2)
emit(state.toResult(Unit.right()))
emit(state.toResult(simpleResult))
}
private val failedSign: suspend FlowCollector<WcSignState<TestSignModel>>.(
@ -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<TestSignModel> {
override suspend fun SignCollector<TestSignModel>.onSign(state: WcSignState<TestSignModel>) {
delay(2)

View file

@ -101,12 +101,12 @@ interface TransactionRepository {
signer: TransactionSigner,
userWalletId: UserWalletId,
network: Network,
): Result<ByteArray>
): com.tangem.blockchain.extensions.Result<ByteArray>
suspend fun prepareForSendMultiple(
transactionData: List<TransactionData>,
signer: TransactionSigner,
userWalletId: UserWalletId,
network: Network,
): Result<List<ByteArray>>
): com.tangem.blockchain.extensions.Result<List<ByteArray>>
}

View file

@ -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<Throwable, ByteArray> {
): Either<SendTransactionError, ByteArray> {
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<TransactionData>,
userWallet: UserWallet,
network: Network,
): Either<Throwable, List<ByteArray>> {
): Either<SendTransactionError, List<ByteArray>> {
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 {

View file

@ -39,7 +39,7 @@ class SendTransactionUseCase(
userWallet: UserWallet,
network: Network,
sendMode: TransactionSender.MultipleTransactionSendMode,
): Either<SendTransactionError?, List<String>> {
): Either<SendTransactionError, List<String>> {
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
val card = userWallet.scanResponse.card
@ -101,7 +101,7 @@ class SendTransactionUseCase(
txData: TransactionData,
userWallet: UserWallet,
network: Network,
): Either<SendTransactionError?, String> {
): Either<SendTransactionError, String> {
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,
)
}
}
}
}
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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
}
}
}

View file

@ -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)
}

View file

@ -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<Throwable, Unit>
suspend fun approve(): Either<WcRequestError, String>
fun reject()
}

View file

@ -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<SignModel>(
sealed interface WcSignStep {
data object PreSign : WcSignStep
data object Signing : WcSignStep
data class Result(val result: Either<Throwable, Unit>) : WcSignStep
data class Result(val result: Either<WcRequestError, String>) : WcSignStep
}

View file

@ -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<Throwable, Unit>, useCase: WcSignUseCase<*>) {
private fun handleSigningError(result: Either<WcRequestError, String>, 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))