Updated on 2026-08-14
This commit is contained in:
parent
9e38389156
commit
7493e56a52
22 changed files with 216 additions and 74 deletions
|
|
@ -72,7 +72,9 @@ internal object WalletConnectDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun defaultWcPairUseCase(): WcPairService = DefaultWcPairService()
|
||||
fun defaultWcPairUseCase(sessionsManager: DefaultWcSessionsManager): WcPairService = DefaultWcPairService(
|
||||
sessionsManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
@ -90,6 +92,7 @@ internal object WalletConnectDataModule {
|
|||
legacyStore: WalletConnectSessionsRepository,
|
||||
getWallets: GetWalletsUseCase,
|
||||
associateNetworks: AssociateNetworksDelegate,
|
||||
analytics: AnalyticsEventHandler,
|
||||
): DefaultWcSessionsManager {
|
||||
val scope = CoroutineScope(SupervisorJob() + dispatchers.io)
|
||||
return DefaultWcSessionsManager(
|
||||
|
|
@ -98,6 +101,7 @@ internal object WalletConnectDataModule {
|
|||
legacyStore = legacyStore,
|
||||
getWallets = getWallets,
|
||||
associateNetworks = associateNetworks,
|
||||
analytics = analytics,
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.data.walletconnect.network.ethereum
|
|||
import arrow.core.left
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.tokenmethods.ApprovalERC20TokenCallData
|
||||
import com.tangem.blockchain.common.Amount as BlockchainAmount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.formatHex
|
||||
|
|
@ -25,13 +24,16 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.*
|
||||
import com.tangem.blockchain.common.Amount as BlockchainAmount
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcEthMethod.SendTransaction,
|
||||
override val respondService: WcRespondService,
|
||||
override val analytics: AnalyticsEventHandler,
|
||||
private val sendTransaction: SendTransactionUseCase,
|
||||
private val ethTxHelper: WcEthTxHelper,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<WcEthTxAction, TransactionData>(),
|
||||
WcTransactionUseCase,
|
||||
|
|
@ -39,10 +41,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
WcMutableFee {
|
||||
|
||||
private var approvalAmount: WcApprovedAmount? = null
|
||||
private var dAppFee = WcEthTxHelper.getDAppFee(
|
||||
network = context.network,
|
||||
txParams = method.transaction,
|
||||
)
|
||||
private var dAppFee: Fee? = null
|
||||
|
||||
override val securityStatus: LceFlow<Throwable, BlockAidTransactionCheck.Result> =
|
||||
blockAidDelegate.getSecurityStatus(
|
||||
|
|
@ -53,7 +52,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
accountAddress = context.accountAddress,
|
||||
).map { lce ->
|
||||
lce.map { result ->
|
||||
val amount = WcEthTxHelper.getApprovedAmount(method.transaction.data, result)
|
||||
val amount = ethTxHelper.getApprovedAmount(method.transaction.data, result)
|
||||
?: return@map BlockAidTransactionCheck.Result.Plain(result)
|
||||
val tokenInfo = amount.tokenInfo
|
||||
this@WcEthSendTransactionUseCase.approvalAmount = WcApprovedAmount(
|
||||
|
|
@ -112,7 +111,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
emit(newState)
|
||||
}
|
||||
|
||||
override fun dAppFee(): Fee.Ethereum.Legacy? {
|
||||
override fun dAppFee(): Fee? {
|
||||
return dAppFee
|
||||
}
|
||||
|
||||
|
|
@ -121,8 +120,9 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun invoke(): Flow<WcSignState<TransactionData>> = flow {
|
||||
val transactionData = WcEthTxHelper.createTransactionData(
|
||||
dAppFee = dAppFee(),
|
||||
dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network)
|
||||
val transactionData = ethTxHelper.createTransactionData(
|
||||
dAppFee = dAppFee,
|
||||
network = context.network,
|
||||
txParams = method.transaction,
|
||||
) ?: return@flow
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.data.walletconnect.network.ethereum
|
|||
import arrow.core.left
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.tokenmethods.ApprovalERC20TokenCallData
|
||||
import com.tangem.blockchain.common.Amount as BlockchainAmount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.formatHex
|
||||
|
|
@ -24,16 +23,15 @@ import com.tangem.domain.walletconnect.usecase.method.*
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.*
|
||||
import com.tangem.blockchain.common.Amount as BlockchainAmount
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
||||
override val respondService: WcRespondService,
|
||||
override val analytics: AnalyticsEventHandler,
|
||||
private val prepareForSend: PrepareForSendUseCase,
|
||||
private val ethTxHelper: WcEthTxHelper,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcEthMethod.SignTransaction,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
|
|
@ -43,10 +41,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
WcMutableFee {
|
||||
|
||||
private var approvalAmount: WcApprovedAmount? = null
|
||||
private var dAppFee = WcEthTxHelper.getDAppFee(
|
||||
network = context.network,
|
||||
txParams = method.transaction,
|
||||
)
|
||||
private var dAppFee: Fee? = null
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
|
|
@ -56,7 +51,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
accountAddress = context.accountAddress,
|
||||
).map { lce ->
|
||||
lce.map { result ->
|
||||
val amount = WcEthTxHelper.getApprovedAmount(method.transaction.data, result)
|
||||
val amount = ethTxHelper.getApprovedAmount(method.transaction.data, result)
|
||||
?: return@map BlockAidTransactionCheck.Result.Plain(result)
|
||||
val tokenInfo = amount.tokenInfo
|
||||
if (!amount.isUnlimited) {
|
||||
|
|
@ -120,15 +115,16 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun invoke(): Flow<WcSignState<TransactionData>> = flow {
|
||||
val transactionData = WcEthTxHelper.createTransactionData(
|
||||
dAppFee = dAppFee(),
|
||||
dAppFee = ethTxHelper.getDAppFee(method.transaction, wallet, network)
|
||||
val transactionData = ethTxHelper.createTransactionData(
|
||||
dAppFee = dAppFee,
|
||||
network = context.network,
|
||||
txParams = method.transaction,
|
||||
) ?: return@flow
|
||||
emitAll(delegate.invoke(transactionData))
|
||||
}
|
||||
|
||||
override fun dAppFee(): Fee.Ethereum.Legacy? {
|
||||
override fun dAppFee(): Fee? {
|
||||
return dAppFee
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,40 +7,43 @@ import com.domain.blockaid.models.transaction.simultation.SimulationData
|
|||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.tokenmethods.ApprovalERC20TokenCallData
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.HEX_PREFIX
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.smartcontract.CompiledSmartContractCallData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.hexToBigDecimal
|
||||
import com.tangem.blockchain.extensions.hexToBigInteger
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.data.common.currency.getCoinId
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetEthSpecificFeeUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcApprovedAmount
|
||||
import com.tangem.domain.walletconnect.model.WcEthTransactionParams
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import javax.inject.Inject
|
||||
|
||||
internal object WcEthTxHelper {
|
||||
private val MANTLE_FEE_ESTIMATE_MULTIPLIER = BigDecimal("1.8")
|
||||
internal class WcEthTxHelper @Inject constructor(
|
||||
private val getSingleCryptoCurrency: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val ethSpecificFee: GetEthSpecificFeeUseCase,
|
||||
) {
|
||||
|
||||
fun getDAppFee(network: Network, txParams: WcEthTransactionParams): Fee.Ethereum.Legacy? {
|
||||
val gasLimit = txParams.gas?.hexToBigDecimal() ?: return null
|
||||
val gasPrice = txParams.gasPrice?.hexToBigDecimal() ?: return null
|
||||
|
||||
val blockchain = network.toBlockchain()
|
||||
|
||||
var feeDecimal = (gasLimit * gasPrice)
|
||||
.movePointLeft(blockchain.decimals())
|
||||
if (blockchain == Blockchain.Mantle) {
|
||||
feeDecimal = feeDecimal.multiply(MANTLE_FEE_ESTIMATE_MULTIPLIER)
|
||||
}
|
||||
|
||||
val feeAmount = Amount(feeDecimal, blockchain)
|
||||
return Fee.Ethereum.Legacy(feeAmount, gasLimit.toBigInteger(), gasPrice.toBigInteger())
|
||||
suspend fun getDAppFee(txParams: WcEthTransactionParams, userWallet: UserWallet, network: Network): Fee? {
|
||||
val gasLimit = txParams.gas?.hexToBigInteger() ?: return null
|
||||
val gasPrice = txParams.gasPrice?.hexToBigInteger()
|
||||
val coinId = getCoinId(network, network.toBlockchain().toCoinId())
|
||||
val currency = getSingleCryptoCurrency.invokeMultiWalletSync(userWallet.walletId, coinId)
|
||||
.map { it.currency }
|
||||
.getOrNull() ?: return null
|
||||
return ethSpecificFee(userWallet, currency, gasLimit, gasPrice)
|
||||
.map { it.minimum }
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
fun createTransactionData(
|
||||
dAppFee: Fee.Ethereum.Legacy?,
|
||||
dAppFee: Fee?,
|
||||
network: Network,
|
||||
txParams: WcEthTransactionParams,
|
||||
): TransactionData.Uncompiled? {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,45 @@
|
|||
package com.tangem.data.walletconnect.pair
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import com.tangem.domain.walletconnect.WcPairService
|
||||
import com.tangem.domain.walletconnect.model.WcPairRequest
|
||||
import com.tangem.domain.walletconnect.repository.WcSessionsManager
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultWcPairService @Inject constructor() : WcPairService {
|
||||
class DefaultWcPairService @Inject constructor(
|
||||
private val sessionsManager: WcSessionsManager,
|
||||
) : WcPairService {
|
||||
private val _pairFlow: Channel<WcPairRequest> = Channel(Channel.BUFFERED)
|
||||
override val pairFlow: Flow<WcPairRequest> = _pairFlow.receiveAsFlow()
|
||||
override val pairFlow: Flow<WcPairRequest> = _pairFlow
|
||||
.receiveAsFlow()
|
||||
.filter(::filterDeeplinkRequestWithSessionTopic)
|
||||
|
||||
override fun pair(request: WcPairRequest) {
|
||||
_pairFlow.trySend(request)
|
||||
}
|
||||
|
||||
// some dApp sends deeplink with session request
|
||||
// we filter session exist, but start dApp pair flow if unexist
|
||||
private suspend fun filterDeeplinkRequestWithSessionTopic(request: WcPairRequest): Boolean {
|
||||
when (request.source) {
|
||||
WcPairRequest.Source.QR,
|
||||
WcPairRequest.Source.CLIPBOARD,
|
||||
WcPairRequest.Source.ETC,
|
||||
-> return true
|
||||
WcPairRequest.Source.DEEPLINK -> Unit
|
||||
}
|
||||
|
||||
val isExistSession = existSessionTopic(request.uri).getOrNull() ?: false
|
||||
return !isExistSession
|
||||
}
|
||||
|
||||
private suspend fun existSessionTopic(uri: String) = runCatching {
|
||||
val sessionTopic = uri.toUri().getQueryParameter("sessionTopic") ?: return@runCatching false
|
||||
val isExistSession = sessionsManager.findSessionByTopic(sessionTopic) != null
|
||||
return@runCatching isExistSession
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,10 @@ import com.domain.blockaid.models.dapp.CheckDAppResult
|
|||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.reown.walletkit.client.Wallet
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
||||
import com.tangem.data.walletconnect.utils.WC_TAG
|
||||
import com.tangem.data.walletconnect.utils.WcSdkSessionConverter
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
||||
import com.tangem.domain.walletconnect.model.*
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
|
||||
import com.tangem.domain.walletconnect.repository.WcSessionsManager
|
||||
|
|
@ -37,6 +37,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
|
|||
|
||||
private val onCallTerminalAction = Channel<TerminalAction>()
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override operator fun invoke(): Flow<WcPairState> {
|
||||
val (uri: String, source: WcPairRequest.Source) = pairRequest
|
||||
return flow {
|
||||
|
|
@ -47,6 +48,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
|
|||
val sdkSessionProposal = sdkDelegate.pair(uri)
|
||||
.onLeft {
|
||||
Timber.tag(WC_TAG).e(it, "Failed to call pair $pairRequest")
|
||||
analytics.send(WcAnalyticEvents.PairFailed)
|
||||
emit(WcPairState.Error(it))
|
||||
}
|
||||
.getOrNull() ?: return@flow
|
||||
|
|
@ -76,7 +78,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
|
|||
}
|
||||
// finish flow if rejected above
|
||||
if (sessionForApprove == null) {
|
||||
analytics.send(WcAnalyticEvents.SessionDisconnected(proposalState.dAppSession))
|
||||
analytics.send(WcAnalyticEvents.SessionDisconnected(proposalState.dAppSession.dAppMetaData))
|
||||
sdkDelegate.rejectSession(sdkSessionProposal.proposerPublicKey)
|
||||
return@flow
|
||||
}
|
||||
|
|
@ -155,7 +157,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
|
|||
analytics.send(
|
||||
WcAnalyticEvents.PairRequested(
|
||||
network = requestedNetworks,
|
||||
verificationInfo.name,
|
||||
verificationInfo,
|
||||
),
|
||||
)
|
||||
val appMetaData = WcAppMetaData(
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import arrow.core.right
|
|||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.reown.walletkit.client.Wallet
|
||||
import com.reown.walletkit.client.WalletKit
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate
|
||||
import com.tangem.data.walletconnect.utils.WC_TAG
|
||||
import com.tangem.data.walletconnect.utils.WcSdkObserver
|
||||
import com.tangem.data.walletconnect.utils.WcSdkSessionConverter
|
||||
import com.tangem.datasource.local.walletconnect.WalletConnectStore
|
||||
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
||||
import com.tangem.domain.walletconnect.model.WcSession
|
||||
import com.tangem.domain.walletconnect.model.WcSessionDTO
|
||||
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
|
||||
|
|
@ -24,12 +26,14 @@ import kotlinx.coroutines.flow.*
|
|||
import timber.log.Timber
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultWcSessionsManager(
|
||||
private val store: WalletConnectStore,
|
||||
private val legacyStore: WalletConnectSessionsRepository,
|
||||
private val getWallets: GetWalletsUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val associateNetworks: AssociateNetworksDelegate,
|
||||
private val analytics: AnalyticsEventHandler,
|
||||
private val scope: CoroutineScope,
|
||||
) : WcSessionsManager, WcSdkObserver {
|
||||
|
||||
|
|
@ -68,6 +72,7 @@ internal class DefaultWcSessionsManager(
|
|||
val topic = session.sdkModel.topic
|
||||
val sdkCall = sdkDisconnectSession(topic)
|
||||
.onRight { onSessionDelete.trySend(Wallet.Model.SessionDelete.Success(topic = topic, reason = "")) }
|
||||
analytics.send(WcAnalyticEvents.SessionDisconnected(session.sdkModel.appMetaData))
|
||||
return sdkCall
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue