Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-20 21:54:46 +04:00
parent 8b75e37e30
commit a9bca131c3
386 changed files with 1369 additions and 1347 deletions

View file

@ -9,10 +9,10 @@ import com.reown.walletkit.client.WalletKit
import com.tangem.data.walletconnect.pair.WcPairSdkDelegate
import com.tangem.data.walletconnect.request.DefaultWcRequestService
import com.tangem.data.walletconnect.sessions.DefaultWcSessionsManager
import com.tangem.data.walletconnect.utils.WcSdkObserver
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.data.walletconnect.utils.WcSdkObserver
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class DefaultWcInitializeUseCase(
private val application: Application,
@ -48,7 +48,7 @@ internal class DefaultWcInitializeUseCase(
application = application,
metaData = appMetaData,
) { error ->
Timber.tag(WC_TAG).e("Error while initializing client: $error")
TangemLogger.withTag(WC_TAG).e("Error while initializing client: $error")
}
WalletKit.initialize(
@ -57,10 +57,10 @@ internal class DefaultWcInitializeUseCase(
val walletDelegate = defineWalletDelegate()
WalletKit.setWalletDelegate(walletDelegate)
wcSdkObservers.forEach { it.onWcSdkInit() }
Timber.tag(WC_TAG).i("onWcSdkInit")
TangemLogger.withTag(WC_TAG).i("onWcSdkInit")
},
onError = { error ->
Timber.tag(WC_TAG).e("Error while initializing Web3Wallet: $error")
TangemLogger.withTag(WC_TAG).e("Error while initializing Web3Wallet: $error")
},
)
}
@ -70,32 +70,32 @@ internal class DefaultWcInitializeUseCase(
get() = super.onSessionAuthenticate
override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) {
Timber.tag(WC_TAG).i("sdk callback onConnectionStateChange isAvailable=${state.isAvailable}")
TangemLogger.withTag(WC_TAG).i("sdk callback onConnectionStateChange isAvailable=${state.isAvailable}")
wcSdkObservers.forEach { it.onConnectionStateChange(state) }
}
override fun onError(error: Wallet.Model.Error) {
Timber.tag(WC_TAG).e(error.throwable, "sdk callback onError")
TangemLogger.withTag(WC_TAG).e("sdk callback onError", error.throwable)
wcSdkObservers.forEach { it.onError(error) }
}
override fun onProposalExpired(proposal: Wallet.Model.ExpiredProposal) {
Timber.tag(WC_TAG).i("sdk callback onProposalExpired $proposal")
TangemLogger.withTag(WC_TAG).i("sdk callback onProposalExpired $proposal")
wcSdkObservers.forEach { it.onProposalExpired(proposal) }
}
override fun onRequestExpired(request: Wallet.Model.ExpiredRequest) {
Timber.tag(WC_TAG).i("sdk callback onRequestExpired $request")
TangemLogger.withTag(WC_TAG).i("sdk callback onRequestExpired $request")
wcSdkObservers.forEach { it.onRequestExpired(request) }
}
override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) {
Timber.tag(WC_TAG).i("sdk callback onSessionDelete $sessionDelete")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionDelete $sessionDelete")
wcSdkObservers.forEach { it.onSessionDelete(sessionDelete) }
}
override fun onSessionExtend(session: Wallet.Model.Session) {
Timber.tag(WC_TAG).i("sdk callback onSessionExtend $session")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionExtend $session")
wcSdkObservers.forEach { it.onSessionExtend(session) }
}
@ -103,7 +103,7 @@ internal class DefaultWcInitializeUseCase(
sessionProposal: Wallet.Model.SessionProposal,
verifyContext: Wallet.Model.VerifyContext,
) {
Timber.tag(WC_TAG).i("sdk callback onSessionProposal $sessionProposal")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionProposal $sessionProposal")
wcSdkObservers.forEach { it.onSessionProposal(sessionProposal, verifyContext) }
}
@ -111,17 +111,17 @@ internal class DefaultWcInitializeUseCase(
sessionRequest: Wallet.Model.SessionRequest,
verifyContext: Wallet.Model.VerifyContext,
) {
Timber.tag(WC_TAG).i("sdk callback onSessionRequest $sessionRequest")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionRequest $sessionRequest")
wcSdkObservers.forEach { it.onSessionRequest(sessionRequest, verifyContext) }
}
override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) {
Timber.tag(WC_TAG).i("sdk callback onSessionSettleResponse $settleSessionResponse")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionSettleResponse $settleSessionResponse")
wcSdkObservers.forEach { it.onSessionSettleResponse(settleSessionResponse) }
}
override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) {
Timber.tag(WC_TAG).i("sdk callback onSessionUpdateResponse $sessionUpdateResponse")
TangemLogger.withTag(WC_TAG).i("sdk callback onSessionUpdateResponse $sessionUpdateResponse")
wcSdkObservers.forEach { it.onSessionUpdateResponse(sessionUpdateResponse) }
}
}

View file

@ -1,17 +1,17 @@
package com.tangem.data.walletconnect.network.solana
import com.tangem.blockchain.extensions.decodeBase58
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.blockchain.extensions.encodeBase64NoWrap
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.utils.converter.Converter
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
internal class SolanaBlockAidAddressConverter @Inject constructor() : Converter<String, String?> {
override fun convert(value: String): String? {
return value.decodeBase58()?.encodeBase64NoWrap() ?: run {
Timber.tag(WC_TAG).e("Error while converting Solana transaction account address")
TangemLogger.withTag(WC_TAG).e("Error while converting Solana transaction account address")
null
}
}

View file

@ -22,13 +22,13 @@ import com.tangem.domain.walletconnect.usecase.method.SignRequirements
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase
import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import okio.ByteString.Companion.decodeBase64
import timber.log.Timber
@Suppress("LongParameterList")
internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
@ -57,13 +57,13 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
val formattedHash = getFormattedHash(hash) // uses for flow sendLargeSolanaTransaction
if (context.session.wallet is UserWallet.Cold && isLargeHash(formattedHash)) {
// workaround for large transactions that cannot be signed directly by card
Timber.w("The transaction hash is too large to be signed directly: ${formattedHash.size} bytes")
TangemLogger.w("The transaction hash is too large to be signed directly: ${formattedHash.size} bytes")
sendLargeSolanaTransactionUseCase(context.session.wallet as UserWallet.Cold, context.network, formattedHash)
.fold(
ifLeft = {
ifLeft = { error ->
analytics.send(SolanaLargeTransactionStatus(SolanaLargeTransactionStatus.Status.Failed))
Timber.e(it.toString())
emit(state.toResult(parseSendError(it).left()))
TangemLogger.e(error.toString())
emit(state.toResult(parseSendError(error).left()))
},
ifRight = {
analytics.send(SolanaLargeTransactionStatus(SolanaLargeTransactionStatus.Status.Success))
@ -115,7 +115,7 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
return try {
SolanaTransactionHelper.removeSignaturesPlaceholders(hash)
} catch (e: Exception) {
Timber.e("Failed to format the hash: ${e.message}")
TangemLogger.e("Failed to format the hash: ${e.message}")
hash
}
}

View file

@ -16,6 +16,7 @@ import com.tangem.domain.walletconnect.model.*
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -24,7 +25,6 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import org.joda.time.DateTime
import org.joda.time.Duration
import timber.log.Timber
import java.net.URI
@Suppress("LongParameterList")
@ -42,7 +42,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
@Suppress("LongMethod")
override operator fun invoke(): Flow<WcPairState> {
return flow {
Timber.tag(WC_TAG).i("start pair flow $pairRequest")
TangemLogger.withTag(WC_TAG).i("start pair flow $pairRequest")
analytics.send(
WcAnalyticEvents.NewPairInitiated(
source = pairRequest.source,
@ -52,22 +52,22 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
emit(WcPairState.Loading)
val pairResult = sdkDelegate.pair(pairRequest.uri)
.onLeft {
Timber.tag(WC_TAG).e(it, "Failed to call pair $pairRequest")
.onLeft { error ->
TangemLogger.withTag(WC_TAG).e("Failed to call pair $pairRequest", error)
analytics.send(
WcAnalyticEvents.PairFailed(
errorCode = it.code,
errorMessage = it.message,
errorCode = error.code,
errorMessage = error.message,
),
)
emit(WcPairState.Error(it))
emit(WcPairState.Error(error))
}
.getOrNull() ?: return@flow
val (sdkSessionProposal, sdkVerifyContext) = pairResult
// check unsupported dApps, just local constant for now, finish if unsupported
if (UnsupportedDApps.list.any { sdkSessionProposal.url.contains(it, ignoreCase = true) }) {
Timber.tag(WC_TAG).i("Unsupported DApp ${sdkSessionProposal.name}")
TangemLogger.withTag(WC_TAG).i("Unsupported DApp ${sdkSessionProposal.name}")
val error = WcPairState.Error(WcPairError.UnsupportedDApp(sdkSessionProposal.name))
emit(error)
return@flow
@ -93,7 +93,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
emit(proposalState)
// wait first terminal action and continue WC pair flow
Timber.tag(WC_TAG).i("pair wait terminal action ${sdkSessionProposal.name}")
TangemLogger.withTag(WC_TAG).i("pair wait terminal action ${sdkSessionProposal.name}")
val terminalAction = onCallTerminalAction.receiveAsFlow().first()
val sessionForApprove: WcSessionApprove? = when (terminalAction) {
is TerminalAction.Approve -> terminalAction.sessionForApprove
@ -139,30 +139,30 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
),
)
proposalState.dAppSession.dAppMetaData
}.onLeft {
}.onLeft { error ->
analytics.send(
WcAnalyticEvents.DAppConnectionFailed(
errorCode = it.code,
errorMessage = it.message,
errorCode = error.code,
errorMessage = error.message,
),
)
sdkDelegate.rejectSession(sdkSessionProposal.proposerPublicKey)
Timber.tag(WC_TAG).e(it, "Failed to approve session ${sdkSessionProposal.name}")
TangemLogger.withTag(WC_TAG).e("Failed to approve session ${sdkSessionProposal.name}", error)
}
emit(WcPairState.Approving.Result(sessionForApprove, either))
}
.catch {
val pairError: WcPairError = when (it) {
is TimeoutCancellationException -> WcPairError.TimeoutException(it.message.orEmpty())
else -> WcPairError.Unknown(it.message.orEmpty())
.catch { throwable ->
val pairError: WcPairError = when (throwable) {
is TimeoutCancellationException -> WcPairError.TimeoutException(throwable.message.orEmpty())
else -> WcPairError.Unknown(throwable.message.orEmpty())
}
emit(WcPairState.Error(pairError))
}
.onCompletion {
if (it != null) {
Timber.tag(WC_TAG).e(it, "Completed with error $pairRequest")
.onCompletion { throwable ->
if (throwable != null) {
TangemLogger.withTag(WC_TAG).e("Completed with error $pairRequest", throwable)
} else {
Timber.tag(WC_TAG).i("Completed successfully $pairRequest")
TangemLogger.withTag(WC_TAG).i("Completed successfully $pairRequest")
}
}
}
@ -191,7 +191,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
)
sdkDelegate.approve(pendingSessionForSave, sessionApprove)
} catch (e: Throwable) {
Timber.tag(WC_TAG).e(e, "Failed to sdk approve session $pairRequest")
TangemLogger.withTag(WC_TAG).e("Failed to sdk approve session $pairRequest", e)
WcPairError.ApprovalFailed(e.message.orEmpty()).left()
}
@ -203,8 +203,8 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
val verificationInfo = when {
verifyContext.validation == Wallet.Model.Validation.INVALID -> CheckDAppResult.UNSAFE
verifyContext.isScam == true -> CheckDAppResult.UNSAFE
else -> blockAidVerifier.verifyDApp(DAppData(sessionProposal.url)).getOrElse {
Timber.tag(WC_TAG).e(it, "Failed to verify DApp ${sessionProposal.name}")
else -> blockAidVerifier.verifyDApp(DAppData(sessionProposal.url)).getOrElse { error ->
TangemLogger.withTag(WC_TAG).e("Failed to verify DApp ${sessionProposal.name}", error)
CheckDAppResult.FAILED_TO_VERIFY
}
}

View file

@ -7,17 +7,17 @@ import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.WalletKit
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.data.walletconnect.utils.WcSdkObserver
import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.data.walletconnect.utils.getDappOriginUrl
import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcPairError.ApprovalFailed
import com.tangem.domain.walletconnect.model.WcPendingApprovalSessionDTO
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.time.Duration.Companion.seconds
@ -91,7 +91,7 @@ internal class WcPairSdkDelegate(
}
fun rejectSession(proposerPublicKey: String) {
Timber.tag(WC_TAG).i("reject session proposerPublicKey = $proposerPublicKey")
TangemLogger.withTag(WC_TAG).i("reject session proposerPublicKey = $proposerPublicKey")
WalletKit.rejectSession(
params = Wallet.Params.SessionReject(
proposerPublicKey = proposerPublicKey,

View file

@ -9,10 +9,10 @@ import com.tangem.data.walletconnect.utils.getDappOriginUrl
import com.tangem.domain.walletconnect.WcRequestService
import com.tangem.domain.walletconnect.model.WcMethodName
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import org.joda.time.DateTime
import timber.log.Timber
internal class DefaultWcRequestService(
private val requestConverters: Set<WcRequestToUseCaseConverter>,
@ -35,10 +35,10 @@ internal class DefaultWcRequestService(
sessionRequest = sessionRequest,
),
)
Timber.tag(WC_TAG).i("handle request $sr")
TangemLogger.withTag(WC_TAG).i("handle request $sr")
val name = requestConverters.firstNotNullOfOrNull { it.toWcMethodName(sr) }
?: WcMethodName.Unsupported(sr.request.method)
Timber.tag(WC_TAG).i("handle request name $name")
TangemLogger.withTag(WC_TAG).i("handle request name $name")
if (name is WcMethodName.Unsupported) {
respondService.rejectRequestNonBlock(sr)
if (name.raw.startsWith("wallet_")) return
@ -56,7 +56,7 @@ internal class DefaultWcRequestService(
val noHaveCached = cachedRequest.none { (_, hashParams) -> hash == hashParams }
if (!noHaveCached) {
Timber.tag(WC_TAG).i("filter request $request")
TangemLogger.withTag(WC_TAG).i("filter request $request")
}
return noHaveCached
}

View file

@ -13,7 +13,7 @@ import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.WcRequestError.Companion.code
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
internal class DefaultWcRequestUseCaseFactory @Inject constructor(
@ -32,9 +32,9 @@ internal class DefaultWcRequestUseCaseFactory @Inject constructor(
?: HandleMethodError.UnknownError("Failed to create WcUseCase").left()
val result = useCase.fold(
ifLeft = {
Timber.tag(WC_TAG).e("$it")
it.left()
ifLeft = { methodError ->
TangemLogger.withTag(WC_TAG).e("$methodError")
methodError.left()
},
ifRight = { (it as? T)?.right() ?: HandleMethodError.Unsupported(WcMethod.Unsupported(request)).left() },
)

View file

@ -10,11 +10,11 @@ import com.tangem.common.extensions.toHexString
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.suspendCancellableCoroutine
import org.joda.time.Duration
import timber.log.Timber
import kotlin.coroutines.resume
internal class DefaultWcRespondService : WcRespondService {
@ -36,21 +36,21 @@ internal class DefaultWcRespondService : WcRespondService {
result = response,
),
),
onSuccess = {
onSuccess = { requestResponse ->
if (continuation.isCompleted) return@respondSessionRequest
val result = when (val response = it.jsonRpcResponse) {
val result = when (val response = requestResponse.jsonRpcResponse) {
is Wallet.Model.JsonRpcResponse.JsonRpcError -> {
Timber.tag(WC_TAG).e("Failed respond $response for request $request")
TangemLogger.withTag(WC_TAG).e("Failed respond $response for request $request")
WcRequestError.WcRespondError(
code = response.code,
message = response.message,
).left()
}
is Wallet.Model.JsonRpcResponse.JsonRpcResult -> {
Timber.tag(WC_TAG).i("Successful respond $response for request $request")
TangemLogger.withTag(WC_TAG).i("Successful respond $response for request $request")
if (response.result == null) {
Timber.tag(WC_TAG).e(
"Response result is null, but it should be String. Casted to empty",
TangemLogger.withTag(WC_TAG).e(
"Response result is null, but requestResponse should be String. Casted to empty",
)
}
(response.result ?: "").right()
@ -58,16 +58,16 @@ internal class DefaultWcRespondService : WcRespondService {
}
continuation.resume(result)
},
onError = {
onError = { error ->
if (continuation.isCompleted) return@respondSessionRequest
Timber.tag(WC_TAG).e(it.throwable, "Failed respond for request $request")
continuation.resume(WcRequestError.UnknownError(it.throwable).left())
TangemLogger.withTag(WC_TAG).e("Failed respond for request $request", error.throwable)
continuation.resume(WcRequestError.UnknownError(error.throwable).left())
},
)
}
override fun rejectRequestNonBlock(request: WcSdkSessionRequest, message: String) {
Timber.tag(WC_TAG).i("reject request $request")
TangemLogger.withTag(WC_TAG).i("reject request $request")
removeCachedRequest(request)
WalletKit.respondSessionRequest(
params = Wallet.Params.SessionRequestResponse(

View file

@ -23,13 +23,13 @@ import com.tangem.domain.walletconnect.repository.WcSessionsManager
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import timber.log.Timber
import kotlin.coroutines.resume
@Suppress("LongParameterList")
@ -91,7 +91,7 @@ internal class DefaultWcSessionsManager(
}
override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) {
Timber.i("onSessionDelete: $sessionDelete")
TangemLogger.i("onSessionDelete: $sessionDelete")
onSessionDelete.trySend(sessionDelete)
}
@ -150,7 +150,7 @@ internal class DefaultWcSessionsManager(
val haveSomeUnknownSdkSessions = unknownSdkSessions.isNotEmpty()
if (haveSomeUnknown) {
Timber.tag(WC_TAG).i("removeUnknownSessions $unknownStoredSessions")
TangemLogger.withTag(WC_TAG).i("removeUnknownSessions $unknownStoredSessions")
store.removeSessions(unknownStoredSessions.toSet())
}
@ -162,7 +162,7 @@ internal class DefaultWcSessionsManager(
val haveEmptyDto = emptyNetworksDto.isNotEmpty()
if (haveEmptyDto) {
Timber.tag(WC_TAG).i("remove sessions without networks $emptyNetworksDto")
TangemLogger.withTag(WC_TAG).i("remove sessions without networks $emptyNetworksDto")
store.removeSessions(emptyNetworksDto)
}
if (haveEmptySessions) {

View file

@ -11,8 +11,8 @@ import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.flow
import timber.log.Timber
import javax.inject.Inject
internal class BlockAidVerificationDelegate @Inject constructor(
@ -72,9 +72,9 @@ internal class BlockAidVerificationDelegate @Inject constructor(
params = params,
),
).fold(
ifLeft = {
Timber.e("Failed to verify transaction: ${it.localizedMessage}")
emit(Lce.Error(it))
ifLeft = { throwable ->
TangemLogger.e("Failed to verify transaction: ${throwable.localizedMessage}")
emit(Lce.Error(throwable))
},
ifRight = {
emit(Lce.Content(it))