diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3458dfc6a4..1188d0e283 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -347,7 +347,6 @@ dependencies { implementation(deps.coil.gif) implementation(deps.coil.svg) implementation(deps.amplitude) - implementation(deps.kotsonGson) implementation(deps.spongecastle.core) implementation(deps.lottie) implementation(deps.compose.accompanist.appCompatTheme) diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index ce53e6dc05..2f4c389818 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -35,6 +35,7 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.repository.OnboardingRepository import com.tangem.domain.settings.repositories.SettingsRepository +import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.builder.ColdUserWalletBuilder import com.tangem.domain.wallets.legacy.UserWalletsListManager @@ -50,7 +51,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository @EntryPoint @InstallIn(SingletonComponent::class) @@ -71,8 +71,6 @@ interface ApplicationEntryPoint { fun getCardScanningFeatureToggles(): CardScanningFeatureToggles - fun getWalletConnect2Repository(): WalletConnect2Repository - fun getScanCardProcessor(): ScanCardProcessor fun getAppCurrencyRepository(): AppCurrencyRepository @@ -151,4 +149,6 @@ interface ApplicationEntryPoint { fun getTangemHotSdk(): TangemHotSdk fun getHotWalletFeatureToggles(): HotWalletFeatureToggles + + fun getWcInitializeUseCase(): WcInitializeUseCase } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 659aaec327..7217bbef8d 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -40,6 +40,7 @@ import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.settings.SetGooglePayAvailabilityUseCase import com.tangem.domain.settings.SetGoogleServicesAvailabilityUseCase @@ -49,12 +50,10 @@ import com.tangem.domain.staking.SendUnsubmittedHashesUseCase import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.features.hotwallet.HotWalletFeatureToggles import com.tangem.features.tangempay.TangemPayFeatureToggles import com.tangem.features.tester.api.TesterMenuLauncher -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles import com.tangem.google.GoogleServicesHelper import com.tangem.operations.backup.BackupService import com.tangem.sdk.api.BackupServiceHolder @@ -64,11 +63,9 @@ import com.tangem.tap.common.DialogManager import com.tangem.tap.common.OnActivityResultCallback import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler -import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.features.main.MainViewModel import com.tangem.tap.proxy.redux.DaggerGraphAction import com.tangem.tap.routing.component.RoutingComponent @@ -116,9 +113,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject lateinit var scanCardUseCase: ScanCardUseCase - @Inject - lateinit var walletConnectInteractor: WalletConnectInteractor - @Inject lateinit var settingsRepository: SettingsRepository @@ -171,9 +165,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject internal lateinit var deeplinkFactory: DeepLinkFactory - @Inject - internal lateinit var walletConnectFeatureToggles: WalletConnectFeatureToggles - @Inject internal lateinit var urlOpener: UrlOpener @@ -183,9 +174,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject internal lateinit var intentProcessor: IntentProcessor - @Inject - internal lateinit var walletConnectLinkIntentHandler: WalletConnectLinkIntentHandler - @Inject internal lateinit var onPushClickedIntentHandler: OnPushClickedIntentHandler @@ -201,7 +189,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject internal lateinit var tangemPayFeatureToggles: TangemPayFeatureToggles - internal val viewModel: MainViewModel by viewModels() + private val viewModel: MainViewModel by viewModels() private lateinit var appThemeModeFlow: SharedFlow @@ -293,7 +281,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { store.dispatch( DaggerGraphAction.SetActivityDependencies( scanCardUseCase = scanCardUseCase, - walletConnectInteractor = walletConnectInteractor, cardSdkConfigRepository = cardSdkConfigRepository, ), ) @@ -369,10 +356,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { private fun initIntentHandlers() { intentProcessor.addHandler(onPushClickedIntentHandler) - - if (!walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) { - intentProcessor.addHandler(walletConnectLinkIntentHandler) - } } private fun updateAppTheme(appThemeMode: AppThemeMode) { diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 4c35b0ee8f..1ed30f5829 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -75,10 +75,12 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.wallet.BuildConfig import dagger.hilt.EntryPoints -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.rekotlin.Store import timber.log.Timber -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository lateinit var store: Store @@ -112,9 +114,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat private val cardScanningFeatureToggles: CardScanningFeatureToggles get() = entryPoint.getCardScanningFeatureToggles() - private val walletConnect2Repository: WalletConnect2Repository - get() = entryPoint.getWalletConnect2Repository() - private val scanCardProcessor: ScanCardProcessor get() = entryPoint.getScanCardProcessor() @@ -237,6 +236,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat private val hotWalletFeatureToggles get() = entryPoint.getHotWalletFeatureToggles() + private val wcInitializeUseCase + get() = entryPoint.getWcInitializeUseCase() + // endregion private val appScope = MainScope() @@ -331,7 +333,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat ) appStateHolder.mainStore = store - walletConnect2Repository.init( + + wcInitializeUseCase.init( projectId = environmentConfigStorage.getConfigSync().walletConnectProjectId, ) } @@ -344,7 +347,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat daggerGraphState = DaggerGraphState( networkConnectionManager = networkConnectionManager, cardScanningFeatureToggles = cardScanningFeatureToggles, - walletConnectRepository = walletConnect2Repository, scanCardProcessor = scanCardProcessor, appCurrencyRepository = appCurrencyRepository, walletManagersFacade = walletManagersFacade, diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 814384c569..c863320c2a 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -5,9 +5,10 @@ import android.content.Context import com.tangem.domain.redux.StateDialog import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.common.ui.* -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog -import com.tangem.tap.features.details.ui.walletconnect.dialogs.* +import com.tangem.tap.common.ui.ScanFailsDialog +import com.tangem.tap.common.ui.SimpleAlertDialog +import com.tangem.tap.common.ui.SimpleCancelableAlertDialog +import com.tangem.tap.common.ui.SimpleOkDialog import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog @@ -59,88 +60,6 @@ class DialogManager : StoreSubscriber { context = context, ) is OnboardingDialog.WalletActivationError -> WalletActivationErrorDialog.create(context, state.dialog) - is WalletConnectDialog.UnsupportedCard -> - SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - messageRes = R.string.wallet_connect_scanner_error_not_valid_card, - context = context, - ) - is WalletConnectDialog.AddNetwork -> { - val message = context.getString( - R.string.wallet_connect_error_missing_blockchains, - ) + state.dialog.networks.joinToString() - SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - message = message, - context = context, - ) - } - is WalletConnectDialog.OpeningSessionRejected -> { - SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - messageRes = R.string.wallet_connect_same_wcuri, - context = context, - ) - } - is WalletConnectDialog.SessionTimeout -> { - SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - messageRes = R.string.wallet_connect_error_timeout, - context = context, - ) - } - is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.data, context) - is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context) - is WalletConnectDialog.BnbTransactionDialog -> - BnbTransactionDialog.create( - preparedData = state.dialog.data, - context = context, - ) - is WalletConnectDialog.UnsupportedNetwork -> { - val warning = if (state.dialog.networks.isNullOrEmpty()) { - context.getString(R.string.wallet_connect_scanner_error_unsupported_network) - } else { - context.getString(R.string.wallet_connect_error_unsupported_blockchains) + - state.dialog.networks.joinToString() - } - SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - message = warning, - context = context, - ) - } - is WalletConnectDialog.UnsupportedDapp -> SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - messageRes = R.string.wallet_connect_error_unsupported_dapp, - context = context, - ) - is WalletConnectDialog.SessionProposalDialog -> { - SessionProposalDialog.create( - sessionProposal = state.dialog.sessionProposal, - networks = state.dialog.networks, - context = context, - onApprove = state.dialog.onApprove, - onReject = state.dialog.onReject, - ) - } - is WalletConnectDialog.SignTransactionDialog -> SignTransactionDialog.create( - preparedData = state.dialog.data, - context = context, - ) - is WalletConnectDialog.SignTransactionsDialog -> SignTransactionsDialog.create( - preparedData = state.dialog.data, - context = context, - ) - is WalletConnectDialog.PairConnectErrorDialog -> SimpleAlertDialog.create( - titleRes = R.string.wallet_connect_title, - message = state.dialog.error.message, - context = context, - ) - is WalletConnectDialog.UnsupportedWcVersion -> SimpleAlertDialog.create( - titleRes = R.string.common_error, - messageRes = R.string.unsupported_wc_version, - context = context, - ) is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create( context = context, scanResponse = state.dialog.scanResponse, diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt deleted file mode 100644 index 24566a94f8..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType - -/** -[REDACTED_AUTHOR] - */ -internal sealed class WalletConnect( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("Wallet Connect", event, params) { - - class ScreenOpened : WalletConnect(event = "WC Screen Opened") - class NewSessionInitiated(source: SourceType) : WalletConnect( - event = "Session Initiated", - params = mapOf( - AnalyticsParam.SOURCE to when (source) { - SourceType.QR -> "QR" - SourceType.DEEPLINK -> "DeepLink" - SourceType.CLIPBOARD -> "Clipboard" - SourceType.ETC -> "etc" - }, - ), - ) - - data object SessionFailed : WalletConnect( - event = "Session Failed", - ) - - class DAppConnectionRequested( - blockchainNames: List, - ) : WalletConnect( - event = "dApp Connection Requested", - params = mapOf( - AnalyticsParam.NETWORKS to blockchainNames.joinToString(","), - ), - ) - - class DAppConnected(dAppName: String, dAppUrl: String, blockchainNames: List) : WalletConnect( - event = "dApp Connected", - params = mapOf( - AnalyticsParam.DAPP_NAME to dAppName, - AnalyticsParam.DAPP_URL to dAppUrl, - AnalyticsParam.BLOCKCHAIN to blockchainNames.joinToString(","), - ), - ) - - class DAppConnectionFailed(dAppName: String, dAppUrl: String, blockchainNames: List) : WalletConnect( - event = "dApp Connection Failed", - params = mapOf( - AnalyticsParam.DAPP_NAME to dAppName, - AnalyticsParam.DAPP_URL to dAppUrl, - AnalyticsParam.BLOCKCHAIN to blockchainNames.joinToString(","), - ), - ) - - class SessionDisconnected(dAppName: String, dAppUrl: String) : WalletConnect( - event = "dApp Disconnected", - params = mapOf( - AnalyticsParam.DAPP_NAME to dAppName, - AnalyticsParam.DAPP_URL to dAppUrl, - ), - ) - - class SignatureRequestHandled( - params: RequestHandledParams, - ) : WalletConnect( - event = "Signature Request Handled", - params = params.toParamsMap(), - ) - - class SignatureRequestReceived( - params: RequestHandledParams, - ) : WalletConnect( - event = "Signature Request Received", - params = params.toParamsMap(), - ) - - class SignatureRequestFailed( - params: RequestHandledParams, - ) : WalletConnect( - event = "Signature Request Failed", - params = params.toParamsMap(), - ) - - data class RequestHandledParams( - val dAppName: String, - val dAppUrl: String, - val methodName: String, - val blockchain: String, - val errorCode: String? = null, - val errorDescription: String? = null, - ) { - fun toParamsMap(): Map { - val validation = if (errorCode == null) Validation.SUCCESS.param else Validation.FAIL.param - val code = errorCode ?: SUCCESS_CODE - return buildMap { - put(AnalyticsParam.DAPP_NAME, dAppName) - put(AnalyticsParam.DAPP_URL, dAppUrl) - put(AnalyticsParam.METHOD_NAME, methodName) - put(AnalyticsParam.BLOCKCHAIN, blockchain) - put(AnalyticsParam.VALIDATION, validation) - put(AnalyticsParam.ERROR_CODE, code) - if (errorDescription != null) { - put(AnalyticsParam.ERROR_DESCRIPTION, errorDescription) - } - } - } - } - - enum class Validation(val param: String) { - SUCCESS("Success"), - FAIL("Fail"), - } - - private companion object { - const val SUCCESS_CODE = "0" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 8bd03da756..6099bad60a 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -2,7 +2,6 @@ package com.tangem.tap.common.redux import com.tangem.tap.common.redux.global.globalReducer import com.tangem.tap.features.details.redux.DetailsReducer -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.welcome.redux.WelcomeReducer import com.tangem.tap.proxy.redux.DaggerGraphReducer import org.rekotlin.Action @@ -14,7 +13,6 @@ fun appReducer(action: Action, state: AppState?): AppState { return AppState( globalState = globalReducer(action, state), detailsState = DetailsReducer.reduce(action, state), - walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState), welcomeState = WelcomeReducer.reduce(action, state), daggerGraphState = DaggerGraphReducer.reduce(action, state), ) diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 427a103a98..7c3065b0f4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -5,8 +5,6 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.legacy.LegacyMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware import com.tangem.tap.features.details.redux.DetailsState -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware import com.tangem.tap.features.welcome.redux.WelcomeMiddleware @@ -19,7 +17,6 @@ import org.rekotlin.StateType data class AppState( val globalState: GlobalState = GlobalState(), val detailsState: DetailsState = DetailsState(), - val walletConnectState: WalletConnectState = WalletConnectState(), val welcomeState: WelcomeState = WelcomeState(), val daggerGraphState: DaggerGraphState = DaggerGraphState(), ) : StateType { @@ -30,7 +27,6 @@ data class AppState( logMiddleware, GlobalMiddleware.handler, DetailsMiddleware().detailsMiddleware, - WalletConnectMiddleware().walletConnectMiddleware, BackupMiddleware().backupMiddleware, WelcomeMiddleware().middleware, LockUserWalletsTimerMiddleware().middleware, diff --git a/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt index cd7becb2e0..b84f990fa6 100644 --- a/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt +++ b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt @@ -4,7 +4,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler -import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -19,10 +18,6 @@ internal object IntentHandlingModule { @Singleton fun provideBackgroundScanIntentHandler(): BackgroundScanIntentHandler = BackgroundScanIntentHandler() - @Provides - @Singleton - fun provideWalletConnectLinkIntentHandler(): WalletConnectLinkIntentHandler = WalletConnectLinkIntentHandler() - @Provides @Singleton fun provideOnPushClickedIntentHandler(analyticsEventHandler: AnalyticsEventHandler): OnPushClickedIntentHandler = diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt deleted file mode 100644 index 0e6ccce36b..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.tangem.tap.domain.walletconnect - -import com.github.salomonbrys.kotson.registerTypeAdapter -import com.google.gson.GsonBuilder -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.calculateSha256 -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTradeOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTransferOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.tradeOrderSerializer -import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData -import com.tangem.tap.features.details.redux.walletconnect.TradeData -import com.tangem.utils.extensions.stripZeroPlainString -import timber.log.Timber - -internal object BnbHelper { - - fun createMessageData(order: WcBinanceTransferOrder): BinanceMessageData.Transfer { - val input = order.msgs.first().inputs.first() - val output = order.msgs.first().inputs.first() - - val currency = - input.coins.firstOrNull()?.denom ?: Blockchain.Binance - val amount = input.coins - .mapNotNull { if (it.denom == currency) it.amount else null } - .sum() - .toBigDecimal() - .movePointLeft(Blockchain.Binance.decimals()) - .stripZeroPlainString() - - val gson = GsonBuilder() - .registerTypeAdapter(tradeOrderSerializer) - .serializeNulls() - .create() - - return BinanceMessageData.Transfer( - outputAddress = output.address, - amount = amount, - address = input.address, - data = gson.toJson(order).toByteArray().calculateSha256(), - ) - } - - fun createMessageData(order: WcBinanceTradeOrder): BinanceMessageData.Trade { - val address = order.msgs.first().sender - - val tradeData = order.msgs.map { - val price = it.price.toBigDecimal() - .movePointLeft(Blockchain.Binance.decimals()) - .stripTrailingZeros() - val quantity = it.quantity.toBigDecimal() - .movePointLeft(Blockchain.Binance.decimals()) - .stripTrailingZeros() - - val amount = price * quantity - val symbol = it.symbol.substringBefore("-") - - TradeData( - price = "$price ${Blockchain.Binance.currency}", - quantity = "$quantity $symbol", - amount = "$amount ${Blockchain.Binance.currency}", - symbol = symbol, - ) - } - val gson = GsonBuilder() - .registerTypeAdapter(tradeOrderSerializer) - .serializeNulls() - .create() - val serialized = gson.toJson(order) - Timber.d(serialized) - - return BinanceMessageData.Trade( - tradeData = tradeData, - address = address, - data = serialized.toByteArray().calculateSha256(), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt deleted file mode 100644 index 80d7b046e9..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.domain.walletconnect - -import com.github.salomonbrys.kotson.fromJson -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.google.gson.JsonParser - -object EthSignHelper { - private val gson: Gson by lazy { - GsonBuilder() - .setPrettyPrinting() - .serializeNulls() - .create() - } - - fun tryToParseEthTypedMessageString(message: String): String? { - return try { - val messageString = message - .replace("\\", "") - .removePrefix("\"") - .removeSuffix("\"") - val messageJson = JsonParser().parse(messageString) - val filteredMap = gson.fromJson>(messageJson) - .filterKeys { it == "domain" || it == "message" } - - gson.toJson(filteredMap) - } catch (exception: Exception) { - null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt deleted file mode 100644 index e6e92ccda4..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ /dev/null @@ -1,529 +0,0 @@ -package com.tangem.tap.domain.walletconnect - -import com.tangem.Message -import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader -import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras -import com.tangem.blockchain.blockchains.ethereum.EthereumUtils -import com.tangem.blockchain.common.* -import com.tangem.blockchain.common.smartcontract.CompiledSmartContractCallData -import com.tangem.blockchain.common.transaction.Fee -import com.tangem.blockchain.extensions.* -import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.common.CompletionResult -import com.tangem.common.extensions.hexToBytes -import com.tangem.common.extensions.toDecompressedPublicKey -import com.tangem.common.extensions.toHexString -import com.tangem.data.walletconnect.network.ethereum.LegacySdkHelper -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.operations.sign.SignHashCommand -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.common.extensions.safeUpdate -import com.tangem.tap.common.extensions.toFormattedString -import com.tangem.tap.domain.walletconnect2.domain.TransactionType -import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction -import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage -import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTradeOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTransferOrder -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData -import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType -import com.tangem.tap.features.details.redux.walletconnect.WcPersonalSignData -import com.tangem.tap.features.details.redux.walletconnect.WcTransactionData -import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData -import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import org.json.JSONArray -import org.json.JSONObject -import timber.log.Timber -import java.math.BigDecimal - -@Suppress("LargeClass") -class WalletConnectSdkHelper { - - private val userWalletsListManager by lazy { - store.inject(DaggerGraphState::generalUserWalletsListManager) - } - - private val userWalletsListRepository by lazy { - store.inject(DaggerGraphState::userWalletsListRepository) - } - - private val hotWalletFeatureToggles by lazy { - store.inject(DaggerGraphState::hotWalletFeatureToggles) - } - - @Suppress("MagicNumber") - suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData { - val transaction = data.transaction - val blockchain = requireNotNull(Blockchain.fromNetworkId(data.networkId)) { "Blockchain not found" } - val walletManager = requireNotNull(getWalletManager(blockchain, data.rawDerivationPath)) { - "WalletManager not found" - } - - walletManager.safeUpdate(isDemoCard()) - val wallet = walletManager.wallet - val balance = requireNotNull(wallet.amounts[AmountType.Coin]?.value) { - "Coin balance not found" - } - - val decimals = wallet.blockchain.decimals() - - val value = (transaction.value ?: "0") - .hexToBigDecimal() - .movePointLeft(decimals) - - requireNotNull(value) { - "Transaction amount is null" - } - - // TODO move fee calculation to SDK getFee() [REDACTED_JIRA] - val gasLimit = getGasLimitFromTx(value, walletManager, transaction, blockchain) - val gasPrice = getGasPrice(walletManager, transaction) - - val feeDecimal = (gasLimit * gasPrice).movePointLeft(decimals) - val total = value + feeDecimal - val feeAmount = Amount(feeDecimal, wallet.blockchain) - - val destinationAddress = requireNotNull(transaction.to) { "Destination address is null" } - - val fee = if (blockchain.isEvm()) { - // TODO [REDACTED_JIRA] - // workaround for Mantle, remove after [REDACTED_JIRA] - val patchedAmount = if (blockchain == Blockchain.Mantle) { - feeAmount.copy(value = feeAmount.value?.multiply(MANTLE_FEE_ESTIMATE_MULTIPLIER)) - } else { - feeAmount - } - Fee.Ethereum.Legacy(patchedAmount, gasLimit.toBigInteger(), gasPrice.toBigInteger()) - } else { - Fee.Common(feeAmount) - } - val transactionData = TransactionData.Uncompiled( - amount = Amount(value, wallet.blockchain), - fee = fee, - sourceAddress = transaction.from, - destinationAddress = destinationAddress, - extras = EthereumTransactionExtras( - callData = CompiledSmartContractCallData(transaction.data.removePrefix(HEX_PREFIX).hexToBytes()), - gasLimit = gasLimit.toBigInteger(), - nonce = transaction.nonce?.hexToBigDecimal()?.toBigInteger(), - ), - ) - - val dialogData = TransactionRequestDialogData( - dAppName = data.metaName, - dAppUrl = data.metaUrl, - amount = value.toFormattedString(decimals), - feeAmount = feeDecimal.toFormattedString(decimals), - totalAmount = total.toFormattedString(decimals), - balance = balance.toFormattedString(decimals), - isEnoughFundsToSend = balance - total >= BigDecimal.ZERO, - topic = data.topic, - id = data.id, - type = data.type, - ) - - return WcTransactionData( - type = data.type, - transaction = transactionData, - topic = data.topic, - id = data.id, - walletManager = walletManager, - dialogData = dialogData, - ) - } - - suspend fun isDemoCard(): Boolean { - val userWallet = getSelectedWallet() ?: return false - return userWallet is UserWallet.Cold && userWallet.scanResponse.isDemoCard() - } - - private suspend fun getWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager? { - val userWallet = getSelectedWallet() ?: return null - val walletManagerFacade = store.inject(DaggerGraphState::walletManagersFacade) - return walletManagerFacade.getOrCreateWalletManager( - userWalletId = userWallet.walletId, - blockchain = blockchain, - derivationPath = derivationPath, - ) - } - - suspend fun completeTransaction(data: WcTransactionData, cardId: String?): String? { - return when (data.type) { - WcEthTransactionType.EthSendTransaction -> sendTransaction(data, cardId) - WcEthTransactionType.EthSignTransaction -> signTransaction(data, cardId) - } - } - - private suspend fun getGasPrice(walletManager: WalletManager, transaction: WcEthereumTransaction): BigDecimal { - val txGasPrice = transaction.gasPrice?.hexToBigDecimal() - if (txGasPrice != null) { - return txGasPrice - } - return when (val result = (walletManager as? EthereumGasLoader)?.getGasPrice()) { - is Result.Success -> result.data.toBigDecimal() - is Result.Failure -> { - (result.error as? Throwable)?.let { Timber.e(it, "getGasPrice failed") } - - error("Unable to get gas price: ${result.error}") - } - null -> error("Gas price is null") - } - } - - private suspend fun getGasLimitFromTx( - value: BigDecimal, - walletManager: WalletManager, - transaction: WcEthereumTransaction, - blockchain: Blockchain, - ): BigDecimal { - return transaction.gas?.hexToBigDecimal() - ?: transaction.gasLimit?.hexToBigDecimal() - ?: getGasLimitFromBlockchain( - value = value, - walletManager = walletManager, - transaction = transaction, - ).increaseForMantleIfNeeded(blockchain) - } - - private suspend fun getGasLimitFromBlockchain( - value: BigDecimal, - walletManager: WalletManager, - transaction: WcEthereumTransaction, - ): BigDecimal { - val gasLimitResult = (walletManager as? EthereumGasLoader)?.getGasLimit( - amount = Amount(value, walletManager.wallet.blockchain), - destination = transaction.to ?: "", - callData = CompiledSmartContractCallData(transaction.data.hexToBytes()), - ) - return when (gasLimitResult) { - is Result.Success -> gasLimitResult.data.toBigDecimal().multiply(BigDecimal("1.2")) - is Result.Failure -> { - (gasLimitResult.error as? Throwable)?.let { Timber.e(it, "getGasLimit failed") } - DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided - } - else -> DEFAULT_MAX_GASLIMIT.toBigDecimal() // Set high gasLimit if not provided - } - } - - // TODO Workaround for Mantle. Remove after [REDACTED_JIRA] - private fun BigDecimal.increaseForMantleIfNeeded(blockchain: Blockchain): BigDecimal { - return if (blockchain == Blockchain.Mantle) { - this.multiply(MANTLE_FEE_ESTIMATE_MULTIPLIER) - } else { - this - } - } - - private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? { - val sdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk - - val result = (data.walletManager as TransactionSender).send( - transactionData = data.transaction, - signer = store.inject(DaggerGraphState::transactionSignerFactory).createTransactionSigner( - cardId = cardId, - sdk = sdk, - twinKey = null, // use null here because twin doesn't support WC - ), - ) - return when (result) { - is Result.Success -> { - val hash = result.data.hash - if (hash.startsWith(HEX_PREFIX)) { - hash - } else { - HEX_PREFIX + hash - } - } - is Result.Failure -> { - Timber.e(result.error as BlockchainSdkError) - null - } - } - } - - private suspend fun signTransaction(data: WcTransactionData, cardId: String?): String? { - val dataToSign = EthereumUtils.buildTransactionToSign( - transactionData = data.transaction, - blockchain = data.walletManager.wallet.blockchain, - ) - - val command = SignHashCommand( - hash = dataToSign.hash, - walletPublicKey = data.walletManager.wallet.publicKey.seedKey, - derivationPath = data.walletManager.wallet.publicKey.derivationPath, - ) - return when ( - val result = tangemSdkManager.runTaskAsync( - runnable = command, - initialMessage = Message(), - cardId = cardId, - preflightReadFilter = null, - ) - ) { - is CompletionResult.Success -> { - val hash = EthereumUtils.prepareTransactionToSend( - signature = result.data.signature, - transactionToSign = dataToSign, - walletPublicKey = data.walletManager.wallet.publicKey, - blockchain = data.walletManager.wallet.blockchain, - ).toHexString() - if (hash.startsWith(HEX_PREFIX)) { - hash - } else { - HEX_PREFIX + hash - } - } - is CompletionResult.Failure -> { - Timber.e(result.error.customMessage) - null - } - } - } - - fun prepareBnbTradeOrder(data: WcBinanceTradeOrder): BinanceMessageData.Trade { - return BnbHelper.createMessageData(data) - } - - fun prepareBnbTransferOrder(data: WcBinanceTransferOrder): BinanceMessageData.Transfer { - return BnbHelper.createMessageData(data) - } - - suspend fun signBnbTransaction( - data: ByteArray, - networkId: String, - derivationPath: String?, - cardId: String?, - ): String? { - val blockchain = Blockchain.fromNetworkId(networkId) ?: return null - val wallet = getWalletManager(blockchain, derivationPath)?.wallet ?: return null - - val command = SignHashCommand( - hash = data, - walletPublicKey = wallet.publicKey.seedKey, - derivationPath = wallet.publicKey.derivationPath, - ) - return when ( - val result = tangemSdkManager.runTaskAsync( - runnable = command, - initialMessage = Message(), - cardId = cardId, - preflightReadFilter = null, - ) - ) { - is CompletionResult.Success -> { - val key = wallet.publicKey.blockchainKey.toDecompressedPublicKey() - getBnbResultString( - key.toHexString(), - result.data.signature.toHexString(), - ) - } - is CompletionResult.Failure -> { - Timber.e(result.error.customMessage) - null - } - } - } - - fun prepareDataForPersonalSign( - message: WcSignMessage, - topic: String, - metaName: String, - id: Long, - ): WcPersonalSignData { - val messageData = when (message.type) { - WcSignMessage.WCSignType.MESSAGE, - WcSignMessage.WCSignType.PERSONAL_MESSAGE, - -> createMessageData(message) - WcSignMessage.WCSignType.TYPED_MESSAGE -> EthereumUtils.makeTypedDataHash(message.data) - WcSignMessage.WCSignType.SOLANA_MESSAGE -> message.data.decodeBase58() - } - - val messageString = message.data.hexToAscii() - ?: EthSignHelper.tryToParseEthTypedMessageString(message.data) - ?: message.data - - val dialogData = PersonalSignDialogData( - dAppName = metaName, - message = messageString, - topic = topic, - id = id, - ) - return WcPersonalSignData( - hash = requireNotNull(messageData) { "Message data must not be null" }, - topic = topic, - id = id, - dialogData = dialogData, - type = message.type, - ) - } - - private fun createMessageData(message: WcSignMessage): ByteArray = LegacySdkHelper.createMessageData(message.data) - - private fun String.hexToAscii(): String? = LegacySdkHelper.hexToAscii(hex = this) - - suspend fun signPersonalMessage( - hashToSign: ByteArray, - networkId: String, - type: WcSignMessage.WCSignType, - derivationPath: String?, - cardId: String?, - ): String? { - val blockchain = Blockchain.fromNetworkId(networkId) ?: return null - val wallet = getWalletManager(blockchain, derivationPath)?.wallet ?: return null - - val command = SignHashCommand( - hash = hashToSign, - walletPublicKey = wallet.publicKey.seedKey, - derivationPath = wallet.publicKey.derivationPath, - ) - return when ( - val result = tangemSdkManager.runTaskAsync( - runnable = command, - cardId = cardId, - preflightReadFilter = null, - ) - ) { - is CompletionResult.Success -> { - val signedHash = result.data.signature - - return when (type) { - WcSignMessage.WCSignType.SOLANA_MESSAGE -> getSolanaResultString(signedHash) - else -> UnmarshalHelper.unmarshalSignatureExtended( - signature = signedHash, - hash = hashToSign, - publicKey = wallet.publicKey.blockchainKey.toDecompressedPublicKey(), - ).asRSVLegacyEVM().toHexString().formatHex() - .lowercase() // use lowercase because some dapps cant handle UPPERCASE - } - } - is CompletionResult.Failure -> { - Timber.e(result.error.customMessage) - null - } - } - } - - /** - * Returns result of signing prepared to send in WC request - */ - suspend fun signTransaction( - hashToSign: ByteArray, - networkId: String, - type: TransactionType, - derivationPath: String?, - cardId: String?, - ): String? { - val blockchain = Blockchain.fromNetworkId(networkId) ?: return null - val wallet = getWalletManager(blockchain, derivationPath)?.wallet ?: return null - - val command = SignHashCommand( - hash = hashToSign, - walletPublicKey = wallet.publicKey.seedKey, - derivationPath = wallet.publicKey.derivationPath, - ) - return when ( - val result = tangemSdkManager.runTaskAsync( - runnable = command, - cardId = cardId, - preflightReadFilter = null, - ) - ) { - is CompletionResult.Success -> { - val signedHash = result.data.signature - - when (type) { - TransactionType.SOLANA_TX -> { - getSolanaResultString(signedHash) - } - } - } - is CompletionResult.Failure -> { - Timber.e(result.error.customMessage) - null - } - } - } - - /** - * Returns result of signing prepared to send in WC request - */ - suspend fun signTransactions( - hashesToSign: List, - networkId: String, - type: TransactionType, - derivationPath: String?, - cardId: String?, - ): String? { - val blockchain = Blockchain.fromNetworkId(networkId) ?: return null - val wallet = getWalletManager(blockchain, derivationPath)?.wallet ?: return null - val sdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk - - val signer = store.inject(DaggerGraphState::transactionSignerFactory).createTransactionSigner( - cardId = cardId, - sdk = sdk, - twinKey = null, - ) - - return when (val signingResult = signer.sign(hashesToSign, wallet.publicKey)) { - is CompletionResult.Failure -> { - Timber.e(signingResult.error.customMessage) - null - } - is CompletionResult.Success -> { - when (type) { - TransactionType.SOLANA_TX -> { - val result = signingResult.data.mapIndexed { index, bytes -> - byteArrayOf(1) + bytes + hashesToSign[index] - } - getSolanaResultTxHashesString(result) - } - } - } - else -> { - null - } - } - } - - private suspend fun getSelectedWallet(): UserWallet? { - return if (hotWalletFeatureToggles.isHotWalletEnabled) { - userWalletsListRepository.selectedUserWalletSync() - } else { - userWalletsListManager.selectedUserWalletSync - } - } - - private fun getSolanaResultString(signedHash: ByteArray) = "{ signature: \"${signedHash.encodeBase58()}\" }" - - /** - * Build json object - * { - * "transactions": [ - * "signed_tx_hash" - * ] - * } - */ - private fun getSolanaResultTxHashesString(signedHashes: List): String { - val result = JSONObject() - val transactions = JSONArray() - signedHashes.forEach { - transactions.put(it.encodeBase64()) - } - result.put("transactions", transactions) - return result.toString() - } - - private fun getBnbResultString(publicKey: String, signature: String) = - "{\"signature\":\"$signature\",\"publicKey\":\"$publicKey\"}" - - private companion object { - const val HEX_PREFIX = "0x" - const val DEFAULT_MAX_GASLIMIT = 350000 - // TODO remove after [REDACTED_JIRA] - private val MANTLE_FEE_ESTIMATE_MULTIPLIER = BigDecimal("1.8") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/TangemWcBlockchainHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/TangemWcBlockchainHelper.kt deleted file mode 100644 index b0e2c44579..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/TangemWcBlockchainHelper.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.app - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.tap.domain.walletconnect2.domain.WcBlockchainHelper -import com.tangem.domain.walletconnect.model.legacy.Account - -internal class TangemWcBlockchainHelper : WcBlockchainHelper { - - private val supportedNonEvmBlockchains = setOf(Blockchain.Solana, Blockchain.SolanaTestnet) - - override fun chainIdToNetworkIdOrNull(chainId: String): String? { - val parsedId = chainId.parseId() ?: return null - val blockchain = parsedId.chainIdToBlockchain() - - return blockchain?.toNetworkId() - } - - override fun chainIdsToBlockchains(chainIds: List): List { - return chainIds.mapNotNull { - it.parseId()?.chainIdToBlockchain() - }.distinct() - } - - override fun chainIdToMissingNetworkNameOrNull(chainId: String): String? { - val parsedId = chainId.parseId() ?: return null - val blockchain = parsedId.chainIdToBlockchain() - - return blockchain?.fullName - ?: if (parsedId.first == EVM_NAMESPACE) { - chainId - } else { - parsedId.first.replaceFirstChar(Char::titlecase) - } - } - - override fun networkIdToChainIdOrNull(networkId: String): List { - val blockchain = Blockchain.fromNetworkId(networkId) - val namespace = blockchain?.getCaip2Namespace() ?: return emptyList() - return blockchain.getCaip2ChainIds().map { - "$namespace$CHAIN_SEPARATOR$it" - } - } - - override fun getNamespaceFromFullChainIdOrNull(chainId: String): String? { - val parsed = chainId.split(CHAIN_SEPARATOR) - return parsed.firstOrNull() - } - - override fun chainIdToFullNameOrNull(chainId: String): String? { - val networkId = chainIdToNetworkIdOrNull(chainId) ?: return null - return Blockchain.fromNetworkId(networkId)?.fullName - } - - override fun chainIdsToAccounts( - walletAddress: String, - chainIds: List, - derivationPath: String?, - ): List { - return chainIds.map { chainId -> - Account(chainId, walletAddress, derivationPath) - } - } - - private fun Blockchain.getCaip2ChainIds(): List { - if (this.isEvm()) return listOfNotNull(this.getChainId()?.toString()) - - return when (this) { - /* - * The WC sample application and documentation use a commented out chain ID. However, in real dApps - * uncommented is used. - * Docs: https://docs.walletconnect.com/advanced/multichain/chain-list - * - * */ - Blockchain.Solana -> listOf("5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ") - Blockchain.SolanaTestnet -> listOf("z4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z") - Blockchain.Polkadot -> listOf("91b171bb158e2d3848fa23a9f1c25182") - Blockchain.Tron -> listOf("0x2b6653dc") - else -> emptyList() - } - } - - private fun Blockchain.getCaip2Namespace(): String? { - return when { - this.isEvm() -> EVM_NAMESPACE - supportedNonEvmBlockchains.contains(this) -> this.toNetworkId().substringBefore(TESTNET_SEPARATOR) - else -> null - } - } - - private fun String.parseId(): Pair? { - val parsed = this.split(CHAIN_SEPARATOR) - if (parsed.size != 2) return null - - return parsed[0] to parsed[1] - } - - private fun Pair.chainIdToBlockchain(): Blockchain? { - return when (first) { - EVM_NAMESPACE -> { - second.toIntOrNull() - ?.let(Blockchain::fromChainId) - } - SOLANA_NAMESPACE -> Blockchain.Solana - else -> { - Blockchain.fromNetworkId(networkId = first) - .takeIf(supportedNonEvmBlockchains::contains) - } - } - } - - private companion object { - const val EVM_NAMESPACE = "eip155" - const val SOLANA_NAMESPACE = "solana" - const val CHAIN_SEPARATOR = ":" - const val TESTNET_SEPARATOR = "/" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/WalletConnectEventsHandlerImpl.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/WalletConnectEventsHandlerImpl.kt deleted file mode 100644 index d84263034d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/app/WalletConnectEventsHandlerImpl.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.app - -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectEventsHandler -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog -import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen -import com.tangem.tap.store -import timber.log.Timber - -internal class WalletConnectEventsHandlerImpl : WalletConnectEventsHandler { - override fun onProposalReceived(proposal: WalletConnectEvents.SessionProposal, networksFormatted: String) { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.SessionProposalDialog( - sessionProposal = proposal, - networks = networksFormatted, - onApprove = { store.dispatchOnMain(WalletConnectAction.ApproveProposal(proposal)) }, - onReject = { store.dispatchOnMain(WalletConnectAction.RejectProposal) }, - ), - ), - ) - } - - override fun onSessionEstablished() { - store.dispatchOnMain(WalletConnectAction.SessionEstablished) - } - - override fun onSessionRejected(error: WalletConnectError) { - store.dispatchOnMain(WalletConnectAction.SessionRejected(error)) - } - - override fun onListOfSessionsUpdated(sessions: List) { - Timber.d("WC2: List of sessions updated. Sessions: $sessions") - store.dispatchOnMain(WalletConnectAction.SessionListUpdated(sessions)) - } - - override fun onSessionRequest(request: WcPreparedRequest) { - store.dispatchOnMain(WalletConnectAction.ShowSessionRequest(request)) - } - - override fun onUnsupportedRequest() { - store.dispatchOnMain(WalletConnectAction.RejectUnsupportedRequest) - } - - override fun onPairConnectError(error: Throwable) { - store.dispatchOnMain(WalletConnectAction.PairConnectErrorAction(error)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt deleted file mode 100644 index 7d1b854d10..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt +++ /dev/null @@ -1,686 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.data - -import android.app.Application -import arrow.core.flatten -import com.reown.android.Core -import com.reown.android.CoreClient -import com.reown.android.relay.ConnectionType -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.UnsupportedDApps -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.walletconnect.WcPairService -import com.tangem.domain.walletconnect.model.WcPairRequest -import com.tangem.domain.walletconnect.model.legacy.Account -import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles -import com.tangem.tap.common.analytics.events.WalletConnect -import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository -import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods -import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer -import com.tangem.tap.domain.walletconnect2.domain.WcRequest -import com.tangem.tap.domain.walletconnect2.domain.models.* -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.emptyFlow -import timber.log.Timber - -internal class DefaultLegacyWalletConnectRepositoryFacade constructor( - private val stub: LegacyWalletConnectRepositoryStub, - private val legacy: DefaultLegacyWalletConnectRepository, - private val walletConnectFeatureToggles: WalletConnectFeatureToggles, - private val wcInitializeUseCase: WcInitializeUseCase, - private val wcPairService: WcPairService, -) : LegacyWalletConnectRepository { - private val isNewWc by lazy { - walletConnectFeatureToggles.isRedesignedWalletConnectEnabled - } - override val events: Flow by lazy { - if (isNewWc) stub.events else legacy.events - } - override val activeSessions: Flow> by lazy { - if (isNewWc) stub.activeSessions else legacy.activeSessions - } - override val currentSessions: List by lazy { - if (isNewWc) stub.currentSessions else legacy.currentSessions - } - - override fun init(projectId: String) { - if (isNewWc) { - wcInitializeUseCase.init(projectId) - } else { - legacy.init(projectId) - } - } - - override fun setUserNamespaces(userNamespaces: Map>) { - if (isNewWc) stub.setUserNamespaces(userNamespaces) else legacy.setUserNamespaces(userNamespaces) - } - - override fun updateSessions() { - if (isNewWc) stub.updateSessions() else legacy.updateSessions() - } - - override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) { - val src = when (source) { - SourceType.QR -> WcPairRequest.Source.QR - SourceType.DEEPLINK -> WcPairRequest.Source.DEEPLINK - SourceType.CLIPBOARD -> WcPairRequest.Source.CLIPBOARD - SourceType.ETC -> WcPairRequest.Source.ETC - } - if (isNewWc) { - wcPairService.pair(WcPairRequest(uri = uri, source = src, userWalletId = userWalletId)) - } else { - legacy.pair(userWalletId = userWalletId, uri = uri, source = source) - } - } - - override fun disconnect(topic: String) { - if (isNewWc) stub.disconnect(topic) else legacy.disconnect(topic) - } - - override fun approve(userNamespaces: Map>, blockchainNames: List) { - if (isNewWc) stub.approve(userNamespaces, blockchainNames) else legacy.approve(userNamespaces, blockchainNames) - } - - override fun reject() { - if (isNewWc) stub.reject() else legacy.reject() - } - - override fun sendRequest(requestData: RequestData, result: String) { - if (isNewWc) stub.sendRequest(requestData, result) else legacy.sendRequest(requestData, result) - } - - override fun rejectRequest(requestData: RequestData, error: WalletConnectError) { - if (isNewWc) stub.rejectRequest(requestData, error) else legacy.rejectRequest(requestData, error) - } - - override fun cancelRequest(topic: String, id: Long, message: String) { - if (isNewWc) stub.cancelRequest(topic, id, message) else legacy.cancelRequest(topic, id, message) - } -} - -internal class LegacyWalletConnectRepositoryStub : LegacyWalletConnectRepository { - override val events: Flow = emptyFlow() - override val activeSessions: Flow> = emptyFlow() - override val currentSessions: List = listOf() - - override fun init(projectId: String) = Unit - - override fun setUserNamespaces(userNamespaces: Map>) = Unit - - override fun updateSessions() = Unit - - override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) = Unit - - override fun disconnect(topic: String) = Unit - - override fun approve(userNamespaces: Map>, blockchainNames: List) = Unit - - override fun reject() = Unit - - override fun sendRequest(requestData: RequestData, result: String) = Unit - - override fun rejectRequest(requestData: RequestData, error: WalletConnectError) = Unit - - override fun cancelRequest(topic: String, id: Long, message: String) = Unit -} - -@Suppress("LargeClass") -internal class DefaultLegacyWalletConnectRepository( - private val application: Application, - private val wcRequestDeserializer: WcJrpcRequestsDeserializer, - private val analyticsHandler: AnalyticsEventHandler, -) : LegacyWalletConnectRepository { - - private var sessionProposal: Wallet.Model.SessionProposal? = null - private var userNamespaces: Map>? = null - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - - private val _events: MutableSharedFlow = MutableSharedFlow() - override val events: Flow = _events - - private val _activeSessions: MutableSharedFlow> = MutableSharedFlow() - override val activeSessions: Flow> = _activeSessions - private val blockchainHelper by lazy { TangemWcBlockchainHelper() } - - override var currentSessions: List = emptyList() - private set - - /** - * @param projectId Project ID at https://cloud.walletconnect.com/ - */ - override fun init(projectId: String) { - val relayUrl = "relay.walletconnect.com" - val serverUrl = "wss://$relayUrl?projectId=$projectId" - val connectionType = ConnectionType.AUTOMATIC - - val appMetaData = Core.Model.AppMetaData( - name = "Tangem", - description = "Tangem Wallet", - url = "tangem.com", - icons = listOf( - "https://user-images.githubusercontent.com/24321494/124071202-72a00900-da58-11eb-935a-dcdab21de52b.png", - ), - redirect = "kotlin-wallet-wc:/request", // Custom Redirect URI - ) - - CoreClient.initialize( - relayServerUrl = serverUrl, - connectionType = connectionType, - application = application, - metaData = appMetaData, - ) { error -> - Timber.e("Error while initializing client: $error") - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ExternalApprovalError(error.throwable.message), - ), - ) - } - } - - WalletKit.initialize( - Wallet.Params.Init(core = CoreClient), - onSuccess = { - val walletDelegate = defineWalletDelegate() - WalletKit.setWalletDelegate(walletDelegate) - }, - onError = { error -> - Timber.e("Error while initializing Web3Wallet: $error") - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ExternalApprovalError(error.throwable.message), - ), - ) - } - }, - ) - } - - private fun defineWalletDelegate(): WalletKit.WalletDelegate { - return object : WalletKit.WalletDelegate { - - @Suppress("LongMethod") - override fun onSessionProposal( - sessionProposal: Wallet.Model.SessionProposal, - verifyContext: Wallet.Model.VerifyContext, - ) { - // Triggered when wallet receives the session proposal sent by a Dapp - Timber.i("sessionProposal: $sessionProposal") - this@DefaultLegacyWalletConnectRepository.sessionProposal = sessionProposal - - if (sessionProposal.name in UnsupportedDApps.list) { - Timber.i("Unsupported DApp") - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.UnsupportedDApp, - ), - ) - } - return - } - - val missingNetworks = findMissingNetworks( - namespaces = sessionProposal.requiredNamespaces, - userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(), - ) - - if (missingNetworks.isNotEmpty()) { - Timber.i("Not added blockchains: $missingNetworks") - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ApprovalErrorMissingNetworks(missingNetworks.toList()), - ), - ) - } - return - } - - val optionalMissingNetwork = findMissingNetworks( - namespaces = sessionProposal.optionalNamespaces, - userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(), - ) - - val optionalWithoutMissingNetworks = removeMissingNetworks( - namespaces = sessionProposal.optionalNamespaces, - userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(), - ) - - // for cases when optionalNamespaces is not empty but we doesn't support none of them - if (optionalMissingNetwork.isNotEmpty() && - optionalWithoutMissingNetworks.isEmpty() && - sessionProposal.requiredNamespaces.isEmpty() // if requiredNamespaces is not empty we can connect - ) { - Timber.i("Not added optional blockchains: $optionalMissingNetwork") - - val unsupportedNetworks = sessionProposal.optionalNamespaces.values - .flatMap { it.chains ?: emptyList() } - .filter { blockchainHelper.chainIdToNetworkIdOrNull(it) == null } - - scope.launch { - val error = if (unsupportedNetworks.isNotEmpty()) { - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ApprovalErrorUnsupportedNetwork(unsupportedNetworks), - ) - } else { - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ApprovalErrorMissingNetworks(optionalMissingNetwork.toList()), - ) - } - _events.emit(error) - } - return - } - - val requiredChainIds = sessionProposal.requiredNamespaces.values.flatMap { it.chains ?: emptyList() } - val optionalChainIds = optionalWithoutMissingNetworks.toList() - val networks = (requiredChainIds + optionalChainIds) - .mapNotNull { blockchainHelper.chainIdToFullNameOrNull(it) } - .distinct() - analyticsHandler.send(WalletConnect.DAppConnectionRequested(networks)) - - scope.launch { - _events.emit( - WalletConnectEvents.SessionProposal( - sessionProposal.name, - sessionProposal.description, - sessionProposal.url, - sessionProposal.icons, - requiredChainIds, - optionalChainIds, - ), - ) - } - } - - override fun onSessionRequest( - sessionRequest: Wallet.Model.SessionRequest, - verifyContext: Wallet.Model.VerifyContext, - ) { - // Triggered when a Dapp sends SessionRequest to sign a transaction or a message - Timber.i("sessionRequest: $sessionRequest") - val request = wcRequestDeserializer.deserialize( - method = sessionRequest.request.method, - params = sessionRequest.request.params, - ) - Timber.i("sessionRequestParsed: $request") - - when (request) { - is WcRequest.AddChain -> { - // we can send approval automatically, because in WC 2.0 the list of chains is approved when - // initial connection is established - sendRequest( - RequestData( - topic = sessionRequest.topic, - requestId = sessionRequest.request.id, - blockchain = sessionRequest.chainId.toString(), - method = WcJrpcMethods.WALLET_ADD_ETHEREUM_CHAIN.code, - ), - result = "", - ) - } - else -> { - val event = WalletConnect.SignatureRequestReceived( - WalletConnect.RequestHandledParams( - dAppName = sessionRequest.peerMetaData?.name ?: "", - dAppUrl = sessionRequest.peerMetaData?.url ?: "", - methodName = sessionRequest.request.method, - blockchain = sessionRequest.chainId - ?.let { blockchainHelper.chainIdToNetworkIdOrNull(it) } ?: "", - ), - ) - analyticsHandler.send(event) - scope.launch { - _events.emit( - WalletConnectEvents.SessionRequest( - request = request, - chainId = sessionRequest.chainId, - topic = sessionRequest.topic, - id = sessionRequest.request.id, - metaUrl = sessionRequest.peerMetaData?.url ?: "", - metaName = sessionRequest.peerMetaData?.name ?: "", - method = sessionRequest.request.method, - ), - ) - } - } - } - } - - override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { - // Triggered when the session is deleted by the peer - if (sessionDelete is Wallet.Model.SessionDelete.Success) { - scope.launch { - _events.emit(WalletConnectEvents.SessionDeleted(sessionDelete.topic)) - updateSessionsInternal().join() - } - } - Timber.i("onSessionDelete: $sessionDelete") - } - - override fun onSessionExtend(session: Wallet.Model.Session) { - Timber.i("onSessionExtend: $session") - } - - override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) { - // Triggered when wallet receives the session settlement response from Dapp - Timber.i("onSessionSettleResponse: $settleSessionResponse") - if (settleSessionResponse is Wallet.Model.SettledSessionResponse.Result) { - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalSuccess( - topic = settleSessionResponse.session.topic, - accounts = userNamespaces?.flatMap { it.value } ?: emptyList(), - ), - ) - updateSessionsInternal().join() - } - } - } - - override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) { - // Triggered when wallet receives the session update response from Dapp - Timber.i("onSessionUpdateResponse: $sessionUpdateResponse") - updateSessionsInternal() - } - - override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { - // Triggered whenever the connection state is changed - Timber.i("onConnectionStateChange: $state") - if (state.isAvailable) updateSessionsInternal() - } - - override fun onError(error: Wallet.Model.Error) { - // Triggered whenever there is an issue inside the SDK - Timber.i("onError: $error") - } - } - } - - override fun setUserNamespaces(userNamespaces: Map>) { - this.userNamespaces = userNamespaces - } - - override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) { - analyticsHandler.send(WalletConnect.NewSessionInitiated(source = source)) - WalletKit.pair( - params = Wallet.Params.Pair(uri), - onSuccess = { - Timber.i("Paired successfully: $it") - }, - onError = { - Timber.e("Error while pairing: $it") - analyticsHandler.send(WalletConnect.SessionFailed) - scope.launch { - _events.emit( - WalletConnectEvents.PairConnectError(it.throwable), - ) - } - }, - ) - } - - override fun approve(userNamespaces: Map>, blockchainNames: List) { - val sessionProposal: Wallet.Model.SessionProposal = requireNotNull(this.sessionProposal) - - val userChains = userNamespaces.flatMap { namespace -> - namespace.value.map { it.chainId to "${it.chainId}:${it.walletAddress}" } - }.groupBy { pair -> pair.first } - .mapValues { entry -> entry.value.map { pair -> pair.second }.toSet() } - - val preparedRequiredNamespaces = sessionProposal.requiredNamespaces - .map { requiredNamespace -> - val accountsRequired = requiredNamespace.value.chains - ?.mapNotNull { chain -> userChains[chain] } - ?.flatten() ?: emptyList() - val optionalNamespace = sessionProposal.optionalNamespaces[requiredNamespace.key] - val accountsOptional = optionalNamespace?.chains - ?.mapNotNull { chain -> userChains[chain] } - ?.flatten() ?: emptyList() - - val methods = (requiredNamespace.value.methods + (optionalNamespace?.methods ?: emptyList())) - .distinct() - requiredNamespace.key to Wallet.Model.Namespace.Session( - accounts = (accountsRequired + accountsOptional).distinct(), - methods = methods, - events = requiredNamespace.value.events, - ) - }.toMap() - - val sessionApproval = Wallet.Params.SessionApprove( - proposerPublicKey = sessionProposal.proposerPublicKey, - namespaces = preparedRequiredNamespaces.ifEmpty { - sessionProposal.createPreparedOptionalNamespaces(userChains) - }.filterValues { it.accounts.isNotEmpty() && it.chains?.isNotEmpty() == true }, - ) - - Timber.i("Session approval is prepared for sending: $sessionApproval") - - WalletKit.approveSession( - params = sessionApproval, - onSuccess = { - Timber.i("Approved successfully: $it") - analyticsHandler.send( - WalletConnect.DAppConnected( - dAppName = sessionProposal.name, - dAppUrl = sessionProposal.url, - blockchainNames = blockchainNames, - ), - ) - }, - onError = { - Timber.e("Error while approving: $it") - analyticsHandler.send( - WalletConnect.DAppConnectionFailed( - dAppName = sessionProposal.name, - dAppUrl = sessionProposal.url, - blockchainNames = blockchainNames, - ), - ) - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ExternalApprovalError(it.throwable.message), - ), - ) - } - }, - ) - } - - private fun Wallet.Model.SessionProposal.createPreparedOptionalNamespaces( - userChains: Map>, - ): Map { - return optionalNamespaces - .map { optionalNamespace -> - val accountsOptional = optionalNamespace.value.chains - ?.mapNotNull { chain -> userChains[chain] } - ?.flatten() ?: emptyList() - - val methods = optionalNamespace.value.methods - optionalNamespace.key to Wallet.Model.Namespace.Session( - accounts = accountsOptional.distinct(), - methods = methods, - chains = userChains.keys - .filter { it.startsWith(optionalNamespace.key) } - .toList(), - events = optionalNamespace.value.events, - ) - } - .toMap() - } - - override fun sendRequest(requestData: RequestData, result: String) { - val session = currentSessions.find { it.topic == requestData.topic } - // Add Ethereum Chain method is processed without user input, skip logging it - if (requestData.method != WcJrpcMethods.WALLET_ADD_ETHEREUM_CHAIN.code) { - analyticsHandler.send( - WalletConnect.SignatureRequestHandled( - WalletConnect.RequestHandledParams( - dAppName = session?.name ?: "", - dAppUrl = session?.url ?: "", - methodName = requestData.method, - blockchain = requestData.blockchain, - ), - ), - ) - } - - val params = Wallet.Params.SessionRequestResponse( - sessionTopic = requestData.topic, - jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcResult( - id = requestData.requestId, - result = result, - ), - ) - Timber.i("Session request response: $params") - - WalletKit.respondSessionRequest( - params = params, - onSuccess = { response -> - Timber.i("Session request responded successfully: $response") - }, - onError = { error -> - Timber.e(error.throwable, "Error while responging session request") - - val handledParams = WalletConnect.RequestHandledParams( - dAppName = session?.name ?: "", - dAppUrl = session?.url ?: "", - methodName = requestData.method, - blockchain = requestData.blockchain, - errorCode = WalletConnectError.ValidationError.error, - errorDescription = error.throwable.message, - ) - analyticsHandler.send(WalletConnect.SignatureRequestFailed(handledParams)) - }, - ) - } - - override fun rejectRequest(requestData: RequestData, error: WalletConnectError) { - val session = currentSessions.find { it.topic == requestData.topic } - - analyticsHandler.send( - WalletConnect.SignatureRequestFailed( - WalletConnect.RequestHandledParams( - dAppName = session?.name ?: "", - dAppUrl = session?.url ?: "", - methodName = requestData.method, - blockchain = requestData.blockchain, - errorCode = error.toString(), - ), - ), - ) - - cancelRequest(requestData.topic, requestData.requestId, error.error) - } - - override fun cancelRequest(topic: String, id: Long, message: String) { - WalletKit.respondSessionRequest( - params = Wallet.Params.SessionRequestResponse( - sessionTopic = topic, - jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError( - id = id, - code = 0, - message = message, - ), - ), - onSuccess = {}, - onError = {}, - ) - } - - override fun reject() { - WalletKit.rejectSession( - params = Wallet.Params.SessionReject( - proposerPublicKey = sessionProposal?.proposerPublicKey ?: "", - reason = "", - ), - onSuccess = { - Timber.i("Rejected successfully: $it") - }, - onError = { - Timber.e("Error while rejecting: $it") - }, - ) - } - - override fun disconnect(topic: String) { - val session = currentSessions.find { it.topic == topic } - WalletKit.disconnectSession( - params = Wallet.Params.SessionDisconnect(topic), - onSuccess = { - analyticsHandler.send( - WalletConnect.SessionDisconnected( - dAppName = session?.name ?: "", - dAppUrl = session?.url ?: "", - ), - ) - updateSessionsInternal() - Timber.i("Disconnected successfully: $it") - }, - onError = { - Timber.e("Error while disconnecting: $it") - }, - ) - } - - fun send(topic: String, id: Long, data: String) { - WalletKit.respondSessionRequest( - params = Wallet.Params.SessionRequestResponse( - sessionTopic = topic, - jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcResult( - id = id, - result = data, - ), - ), - onError = {}, - onSuccess = {}, - ) - } - - override fun updateSessions() { - updateSessionsInternal() - } - - private fun updateSessionsInternal(): Job = scope.launch { - val availableSessions = WalletKit.getListOfActiveSessions() - .map { - WalletConnectSession( - topic = it.topic, - icon = it.metaData?.icons?.firstOrNull(), - name = it.metaData?.name, - url = it.metaData?.url, - ) - } - Timber.i("Available sessions: $availableSessions") - currentSessions = availableSessions - _activeSessions.emit(availableSessions) - } - - private fun findMissingNetworks( - namespaces: Map, - userNamespaces: Map>, - ): Collection { - val requiredChains = namespaces.values.flatMap { it.chains ?: emptyList() } - val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } } - return requiredChains.subtract(userChains.toSet()) - } - - private fun removeMissingNetworks( - namespaces: Map, - userNamespaces: Map>, - ): Collection { - val wcProvidedChains = namespaces.values.flatMap { it.chains ?: emptyList() } - val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } } - return wcProvidedChains.intersect(userChains.toSet()) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultWalletConnectSessionsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultWalletConnectSessionsRepository.kt deleted file mode 100644 index 108492f2de..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultWalletConnectSessionsRepository.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.data - -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types -import com.tangem.datasource.files.FileReader -import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository -import com.tangem.domain.walletconnect.model.legacy.Session -import timber.log.Timber - -internal class DefaultWalletConnectSessionsRepository( - private val moshi: Moshi, - private val fileReader: FileReader, -) : - WalletConnectSessionsRepository { - - private val sessionsAdapter: JsonAdapter> = moshi.adapter( - Types.newParameterizedType(List::class.java, Session::class.java), - ) - - override suspend fun loadSessions(userWallet: String): List { - return try { - val fileContent = fileReader.readFile(getFileNameForUserWallet(userWallet)) - sessionsAdapter.fromJson(fileContent) ?: emptyList() - } catch (exception: Exception) { - Timber.d(exception) - emptyList() - } - } - - override suspend fun saveSession(userWallet: String, session: Session) { - val updatedList = loadSessions(userWallet).plus(session) - writeSessionToFile(sessions = updatedList, userWallet = userWallet) - } - - override suspend fun removeSession(userWallet: String, topic: String) { - val updatedList = loadSessions(userWallet).filterNot { it.topic == topic } - writeSessionToFile(sessions = updatedList, userWallet = userWallet) - } - - private fun writeSessionToFile(sessions: List, userWallet: String) { - val serialized = sessionsAdapter.toJson(sessions) - try { - fileReader.rewriteFile(serialized, getFileNameForUserWallet(userWallet)) - } catch (exception: Exception) { - Timber.e(exception) - } - } - - companion object { - private const val FILE_NAME_PREFIX = "wc_2" - - private fun getFileNameForUserWallet(userWallet: String): String { - return "$FILE_NAME_PREFIX-${userWallet.uppercase()}" - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/di/WalletConnectInteractorModule.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/di/WalletConnectInteractorModule.kt deleted file mode 100644 index 6c9e9f189d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/di/WalletConnectInteractorModule.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.di - -import android.app.Application -import com.squareup.moshi.Moshi -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.configtoggle.feature.FeatureTogglesManager -import com.tangem.datasource.di.SdkMoshi -import com.tangem.datasource.files.FileReader -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.walletconnect.WcPairService -import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository -import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase -import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles -import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper -import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper -import com.tangem.tap.domain.walletconnect2.app.WalletConnectEventsHandlerImpl -import com.tangem.tap.domain.walletconnect2.data.DefaultLegacyWalletConnectRepository -import com.tangem.tap.domain.walletconnect2.data.DefaultLegacyWalletConnectRepositoryFacade -import com.tangem.tap.domain.walletconnect2.data.DefaultWalletConnectSessionsRepository -import com.tangem.tap.domain.walletconnect2.data.LegacyWalletConnectRepositoryStub -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor -import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer -import com.tangem.tap.domain.walletconnect2.toggles.DefaultWalletConnectFeatureToggles -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object WalletConnectInteractorModule { - - @Provides - @Singleton - fun provideWalletConnectInteractor( - wcRepository: LegacyWalletConnectRepository, - wcSessionsRepository: WalletConnectSessionsRepository, - currenciesRepository: CurrenciesRepository, - walletManagersFacade: WalletManagersFacade, - walletConnectFeatureToggles: WalletConnectFeatureToggles, - coroutineDispatcherProvider: CoroutineDispatcherProvider, - getSelectedWalletUseCase: GetSelectedWalletUseCase, - ): WalletConnectInteractor { - return WalletConnectInteractor( - handler = WalletConnectEventsHandlerImpl(), - walletConnectRepository = wcRepository, - sessionsRepository = wcSessionsRepository, - sdkHelper = WalletConnectSdkHelper(), - blockchainHelper = TangemWcBlockchainHelper(), - currenciesRepository = currenciesRepository, - walletManagersFacade = walletManagersFacade, - getSelectedWalletUseCase = getSelectedWalletUseCase, - dispatchers = coroutineDispatcherProvider, - walletConnectFeatureToggles = walletConnectFeatureToggles, - ) - } -} - -@Module -@InstallIn(SingletonComponent::class) -internal object WalletConnectModule { - - @Provides - @Singleton - fun provideWalletConnectFeatureToggles(featureTogglesManager: FeatureTogglesManager): WalletConnectFeatureToggles { - return DefaultWalletConnectFeatureToggles(featureTogglesManager) - } - - @Provides - @Singleton - fun provideWalletConnectRepository( - application: Application, - wcRequestDeserializer: WcJrpcRequestsDeserializer, - analyticsHandler: AnalyticsEventHandler, - walletConnectFeatureToggles: WalletConnectFeatureToggles, - wcInitializeUseCase: WcInitializeUseCase, - wcPairService: WcPairService, - ): LegacyWalletConnectRepository { - val legacy = DefaultLegacyWalletConnectRepository( - application = application, - wcRequestDeserializer = wcRequestDeserializer, - analyticsHandler = analyticsHandler, - ) - val stub = LegacyWalletConnectRepositoryStub() - return DefaultLegacyWalletConnectRepositoryFacade( - stub, - legacy, - walletConnectFeatureToggles, - wcInitializeUseCase, - wcPairService, - ) - } - - @Provides - @Singleton - fun provideWalletConnectSessionsRepository( - @SdkMoshi moshi: Moshi, - fileReader: FileReader, - ): WalletConnectSessionsRepository { - return DefaultWalletConnectSessionsRepository( - moshi = moshi, - fileReader = fileReader, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt deleted file mode 100644 index 55b4899aa9..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/LegacyWalletConnectRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.walletconnect.model.legacy.Account -import com.tangem.tap.domain.walletconnect2.domain.models.* -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType -import kotlinx.coroutines.flow.Flow - -interface LegacyWalletConnectRepository { - - val events: Flow - - val activeSessions: Flow> - - val currentSessions: List - - fun init(projectId: String) - - fun setUserNamespaces(userNamespaces: Map>) - - fun updateSessions() - - fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) - - fun disconnect(topic: String) - - fun approve(userNamespaces: Map>, blockchainNames: List) - - fun reject() - - fun sendRequest(requestData: RequestData, result: String) - - fun rejectRequest(requestData: RequestData, error: WalletConnectError) - - fun cancelRequest(topic: String, id: Long, message: String = "") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectEventsHandler.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectEventsHandler.kt deleted file mode 100644 index b9414d247e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectEventsHandler.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen - -interface WalletConnectEventsHandler { - fun onProposalReceived(proposal: WalletConnectEvents.SessionProposal, networksFormatted: String) - - fun onSessionEstablished() - - fun onSessionRejected(error: WalletConnectError) - - fun onListOfSessionsUpdated(sessions: List) - - fun onSessionRequest(request: WcPreparedRequest) - - fun onUnsupportedRequest() - - fun onPairConnectError(error: Throwable) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt deleted file mode 100644 index 395ad23885..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ /dev/null @@ -1,457 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import arrow.core.flatten -import com.tangem.blockchainsdk.utils.toBlockchain -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.network.Network -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.isMultiCurrency -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.walletconnect.model.legacy.Account -import com.tangem.domain.walletconnect.model.legacy.Session -import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository -import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.filterNotNull -import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper -import com.tangem.tap.domain.walletconnect2.domain.models.* -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* -import timber.log.Timber -import java.util.Stack - -@Suppress("LargeClass", "LongParameterList") -class WalletConnectInteractor( - private val handler: WalletConnectEventsHandler, - private val walletConnectRepository: LegacyWalletConnectRepository, - private val sessionsRepository: WalletConnectSessionsRepository, - private val sdkHelper: WalletConnectSdkHelper, - private val dispatchers: CoroutineDispatcherProvider, - private val walletManagersFacade: WalletManagersFacade, - private val currenciesRepository: CurrenciesRepository, - private val walletConnectFeatureToggles: WalletConnectFeatureToggles, - private val getSelectedWalletUseCase: GetSelectedWalletUseCase, - val blockchainHelper: WcBlockchainHelper, -) { - private val isNewWc by lazy { walletConnectFeatureToggles.isRedesignedWalletConnectEnabled } - - private var isWalletConnectReadyForDeepLinks = false - - private val wcScope = CoroutineScope( - SupervisorJob() + dispatchers.io + CoroutineExceptionHandler { _, throwable -> - Timber.e("CoroutineException: from: LISTENER SCOPE, exception: $throwable") - }, - ) - - private val listenerScope = CoroutineScope( - SupervisorJob() + dispatchers.io + CoroutineExceptionHandler { _, throwable -> - Timber.e("CoroutineException: from: LISTENER SCOPE, exception: $throwable") - }, - ) - - /** Stack of deeplinks to handle if user wallet is not selected or cryptocurrency statuses are not available */ - private val deeplinkStack: Stack = Stack() - - private val events = walletConnectRepository.events - private val sessions = walletConnectRepository.activeSessions - - private var userWalletId: String = "" - private var cardId: String? = null - - private var currentRequest: WalletConnectEvents.SessionRequest? = null - private val sessionRequestConverter = WcSessionRequestConverter( - blockchainHelper = blockchainHelper, - sessionsRepository = sessionsRepository, - sdkHelper = sdkHelper, - ) - - init { - getSelectedWalletUseCase().onRight { userWalletFlow -> - userWalletFlow - .conflate() - .distinctUntilChanged() - .onEach(::initWithWallet) - .flowOn(dispatchers.io) - .launchIn(wcScope) - } - } - - private fun initWithWallet(userWallet: UserWallet) { - if (isNewWc) return - if (userWallet.isMultiCurrency) { - Timber.i("WalletConnect: initialize and setup networks for ${userWallet.walletId}") - startListeningWc(userWallet.walletId.stringValue, getCardId(userWallet)) - subscribeOnCurrenciesUpdates(userWallet) - } - } - - private fun subscribeOnCurrenciesUpdates(userWallet: UserWallet) { - currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWallet.walletId) - .conflate() - .distinctUntilChanged() - .onEach { currencies -> - setupUserChains(userWallet, currencies) - } - .flowOn(dispatchers.io) - .launchIn(wcScope) - } - - private suspend fun setupUserChains(userWallet: UserWallet, currencies: List) { - val accounts = getAccountsForWc( - userWallet = userWallet, - networks = currencies.map { it.network }.distinct(), - ) - setUserChains(accounts) - handleDeeplinkStack(accounts) - } - - private fun startListeningWc(userWalletId: String, cardId: String?) { - this.userWalletId = userWalletId - this.cardId = cardId - listenerScope.coroutineContext.cancelChildren() - listenerScope.launch { - launch { subscribeToEvents() } - launch { subscribeToSessions() } - walletConnectRepository.updateSessions() - } - } - - private fun setUserChains(accounts: List) { - val userNamespaces: Map> = accounts - .groupBy { account -> - blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId) - ?.let { NetworkNamespace(it) } - }.filterNotNull() - walletConnectRepository.setUserNamespaces(userNamespaces) - } - - private fun handleDeeplinkStack(accounts: List) { - runCatching { - if (accounts.isEmpty()) return - isWalletConnectReadyForDeepLinks = true - if (deeplinkStack.empty()) return - val lastDeeplink = deeplinkStack.pop() - val action = WalletConnectAction - .OpenSession( - wcUri = lastDeeplink, - source = WalletConnectAction.OpenSession.SourceType.DEEPLINK, - userWalletId = UserWalletId(userWalletId), - ) - store.dispatchOnMain(action) - }.onFailure { - Timber.e("WC deeplink handling failed. $it") - } - } - - private suspend fun subscribeToEvents() { - events - .onEach { wcEvent -> - Timber.i("WalletConnect: event: $wcEvent") - when (wcEvent) { - is WalletConnectEvents.SessionProposal -> { - val unsupportedNetworks = wcEvent.requiredChainIds - .filter { blockchainHelper.chainIdToNetworkIdOrNull(it) == null } - if (unsupportedNetworks.isNotEmpty()) { - val error = WalletConnectError.ApprovalErrorUnsupportedNetwork(unsupportedNetworks) - handler.onSessionRejected(error) - return@onEach - } - val networksFormatted = (wcEvent.requiredChainIds + wcEvent.optionalChainIds) - .mapNotNull { blockchainHelper.chainIdToFullNameOrNull(it) } - .distinct() - .toString() - handler.onProposalReceived(proposal = wcEvent, networksFormatted = networksFormatted) - } - is WalletConnectEvents.SessionApprovalError -> { - val error = when (wcEvent.error) { - is WalletConnectError.ApprovalErrorMissingNetworks -> { - val missingNetworks = wcEvent.error.missingChains.mapNotNull { - blockchainHelper.chainIdToMissingNetworkNameOrNull(it) - } - - WalletConnectError.ApprovalErrorAddNetwork(missingNetworks) - } - else -> wcEvent.error - } - handler.onSessionRejected(error) - } - is WalletConnectEvents.SessionApprovalSuccess -> { - sessionsRepository.saveSession( - userWallet = userWalletId, - session = Session( - accounts = wcEvent.accounts, - topic = wcEvent.topic, - ), - ) - walletConnectRepository.updateSessions() - handler.onSessionEstablished() - } - is WalletConnectEvents.SessionDeleted -> { - sessionsRepository.removeSession(userWalletId, wcEvent.topic) - } - is WalletConnectEvents.SessionRequest -> { - handleRequest(wcEvent) - } - is WalletConnectEvents.PairConnectError -> { - handler.onPairConnectError(wcEvent.error) - } - } - } - .flowOn(dispatchers.io) - .collect() - } - - private suspend fun subscribeToSessions() { - sessions - .onEach { listOfSessions -> - val relevantTopics = getTopicsForUserWallet(userWalletId, sessionsRepository) - val filteredSessions = filterSessionsForUserWallet(listOfSessions, relevantTopics) - handler.onListOfSessionsUpdated(filteredSessions) - } - .flowOn(dispatchers.io) - .collect() - } - - private fun filterSessionsForUserWallet( - availableSessions: List, - relevantTopics: List, - ): List { - return availableSessions.filter { relevantTopics.contains(it.topic) } - .map { - WcSessionForScreen( - description = it.name ?: "", - sessionId = it.topic, - ) - } - } - - private suspend fun getTopicsForUserWallet( - userWalletId: String, - repository: WalletConnectSessionsRepository, - ): List { - return repository.loadSessions(userWalletId).map { it.topic } - } - - fun approveSessionProposal(accounts: List) { - if (isNewWc) return - Timber.i("Approve session proposal: $accounts") - val userNamespaces: Map> = accounts - .groupBy { account -> - blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId) - ?.let { NetworkNamespace(it) } - }.filterNotNull() - - val blockchainNames = blockchainHelper.chainIdsToBlockchains(userNamespaces.values.flatten().map { it.chainId }) - .map { it.fullName } - walletConnectRepository.approve( - userNamespaces = userNamespaces, - blockchainNames = blockchainNames, - ) - } - - fun rejectSessionProposal() { - if (isNewWc) return - Timber.i("Reject session proposal") - walletConnectRepository.reject() - } - - fun disconnectSession(topic: String) { - if (isNewWc) return - Timber.i("Disconnect session: $topic") - walletConnectRepository.disconnect(topic) - } - - fun cancelRequest(topic: String, id: Long) { - if (isNewWc) return - Timber.i("Cancel request: $topic, $id") - walletConnectRepository.cancelRequest(topic, id) - } - - private suspend fun handleRequest(sessionRequest: WalletConnectEvents.SessionRequest) { - val error: WalletConnectError? = when { - sessionsRepository.loadSessions(userWalletId).none { it.topic == sessionRequest.topic } -> { - WalletConnectError.WrongUserWallet - } - sessionRequest.request is WcRequest.CustomRequest -> { - WalletConnectError.UnsupportedMethod - } - else -> { - null - } - } - val networkId = sessionRequest.chainId?.let { blockchainHelper.chainIdToNetworkIdOrNull(it) } ?: "" - val requestData = RequestData( - topic = sessionRequest.topic, - requestId = sessionRequest.id, - blockchain = networkId, - method = sessionRequest.method, - ) - - if (error != null) { - walletConnectRepository.rejectRequest(requestData, error) - return - } - - when (sessionRequest.request) { - is WcRequest.BnbCancel -> Unit - is WcRequest.BnbTxConfirm -> walletConnectRepository.sendRequest( - requestData = requestData, - result = "", - ) - else -> { - currentRequest = sessionRequest - - val data = prepareRequestData(sessionRequest).getOrElse { e -> - val wrappedError = e as? WalletConnectError ?: WalletConnectError.UnknownError( - message = e.localizedMessage ?: "Unknown error", - ) - - walletConnectRepository.rejectRequest(requestData, wrappedError) - handler.onSessionRejected(wrappedError) - return - } - - handler.onSessionRequest(data) - } - } - } - - suspend fun continueWithRequest(request: WcPreparedRequest) { - if (isNewWc) return - val currentRequest = this.currentRequest - if (currentRequest == null || request.topic != currentRequest.topic) return - - val networkId = blockchainHelper.chainIdToNetworkIdOrNull(currentRequest.chainId.orEmpty()) ?: return - val signingResultData = when (request) { - is WcPreparedRequest.BnbTransaction -> sdkHelper.signBnbTransaction( - data = request.preparedRequestData.data.data, - networkId = networkId, - derivationPath = request.derivationPath, - cardId = cardId, - ) - is WcPreparedRequest.EthTransaction -> sdkHelper.completeTransaction( - data = request.preparedRequestData, - cardId = cardId, - ) - is WcPreparedRequest.EthSign -> sdkHelper.signPersonalMessage( - hashToSign = request.preparedRequestData.hash, - networkId = networkId, - type = request.preparedRequestData.type, - derivationPath = request.derivationPath, - cardId = cardId, - ) - is WcPreparedRequest.SolanaSignTransaction -> sdkHelper.signTransaction( - hashToSign = request.preparedRequestData.hashToSign, - networkId = networkId, - type = request.preparedRequestData.type, - derivationPath = request.derivationPath, - cardId = cardId, - ) - is WcPreparedRequest.SolanaSignMultipleTransactions -> sdkHelper.signTransactions( - hashesToSign = request.preparedRequestData.hashesToSign, - networkId = networkId, - type = request.preparedRequestData.type, - derivationPath = request.derivationPath, - cardId = cardId, - ) - } - - val requestData = RequestData( - topic = request.topic, - requestId = request.requestId, - blockchain = networkId, - method = currentRequest.method, - ) - - if (signingResultData == null) { - walletConnectRepository.rejectRequest(requestData, WalletConnectError.SigningError) - } else { - walletConnectRepository.sendRequest( - requestData = requestData, - result = signingResultData, - ) - } - } - - /** - * Handles Wallet Connect deep links. - * If wallet connect is able to handle the deeplink, session is started with deeplink. - * Otherwise, deeplink is stored until wallet connect is ready to handle it. - * - * @param deeplink deeplink to handle - */ - fun addDeeplink(deeplink: String) { - if (isNewWc) return - val deeplinkRegex = Regex(WC_PARAM_REGEX) - val matched = deeplinkRegex.findAll(deeplink) - val sessionTopic = matched.firstOrNull { it.value.contains(WC_TOPIC_QUERY_NAME) }?.groupValues?.lastOrNull() - - val isAlreadyActiveSessionTopic = walletConnectRepository.currentSessions.any { session -> - session.topic == sessionTopic - } - - if (isAlreadyActiveSessionTopic && sessionTopic != null) { - Timber.i("WC already has an active session topic: $deeplink") - return - } - - if (isWalletConnectReadyForDeepLinks) { - val action = WalletConnectAction.OpenSession( - wcUri = deeplink, - source = WalletConnectAction.OpenSession.SourceType.DEEPLINK, - userWalletId = UserWalletId(userWalletId), - ) - store.dispatchOnMain(action) - } else { - deeplinkStack.push(deeplink) - } - } - - private fun getCardId(userWallet: UserWallet): String? { - return if (userWallet is UserWallet.Cold && userWallet.scanResponse.card.backupStatus?.isActive != true) { - userWallet.cardId - } else { // if wallet has backup, any card from wallet can be used to sign - null - } - } - - private suspend fun getAccountsForWc(userWallet: UserWallet, networks: List): List { - val walletManagers = networks.mapNotNull { - val blockchain = it.toBlockchain() - - walletManagersFacade.getOrCreateWalletManager( - userWalletId = userWallet.walletId, - blockchain = blockchain, - derivationPath = it.derivationPath.value, - ) - } - return walletManagers.flatMap { - val wallet = it.wallet - val chainIds = blockchainHelper.networkIdToChainIdOrNull(wallet.blockchain.toNetworkId()) - blockchainHelper.chainIdsToAccounts( - walletAddress = wallet.address, - chainIds = chainIds, - derivationPath = wallet.publicKey.derivationPath?.rawPath, - ) - } - } - - private suspend fun prepareRequestData( - sessionRequest: WalletConnectEvents.SessionRequest, - ): Result { - return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId) - } - - private companion object { - const val WC_TOPIC_QUERY_NAME = "sessionTopic" - const val WC_PARAM_REGEX = "([a-zA-Z\\d-]+)=([a-zA-Z\\d]+)" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcBlockchainHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcBlockchainHelper.kt deleted file mode 100644 index 74241e4cf6..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcBlockchainHelper.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.walletconnect.model.legacy.Account - -interface WcBlockchainHelper { - fun chainIdToNetworkIdOrNull(chainId: String): String? - - fun chainIdToMissingNetworkNameOrNull(chainId: String): String? - - fun networkIdToChainIdOrNull(networkId: String): List - - fun getNamespaceFromFullChainIdOrNull(chainId: String): String? - - fun chainIdToFullNameOrNull(chainId: String): String? - - fun chainIdsToAccounts(walletAddress: String, chainIds: List, derivationPath: String?): List - - fun chainIdsToBlockchains(chainIds: List): List -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt deleted file mode 100644 index 3a79b205ed..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcJrpcMethods.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.squareup.moshi.* -import com.tangem.datasource.di.SdkMoshi -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceCancelOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTradeOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTransferOrder -import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTxConfirmParam -import com.tangem.tap.domain.walletconnect2.domain.models.solana.SolanaSignMessage -import com.tangem.tap.domain.walletconnect2.domain.models.solana.SolanaTransactionRequest -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton - -internal enum class WcJrpcMethods(val code: String) { - - ETH_SIGN("eth_sign"), - ETH_PERSONAL_SIGN("personal_sign"), - ETH_SIGN_TYPE_DATA("eth_signTypedData"), - ETH_SIGN_TYPE_DATA_V4("eth_signTypedData_v4"), - ETH_SIGN_TRANSACTION("eth_signTransaction"), - ETH_SEND_TRANSACTION("eth_sendTransaction"), - - BNB_SIGN("bnb_sign"), - BNB_TRANSACTION_CONFIRM("bnb_tx_confirmation"), - SIGN_TRANSACTION("trust_signTransaction"), - WALLET_ADD_ETHEREUM_CHAIN("wallet_addEthereumChain"), - - SOLANA_SIGN_TX("solana_signTransaction"), - SOLANA_SIGN_MESSAGE("solana_signMessage"), - SOLANA_SIGN_ALL_TX("solana_signAllTransactions"), - ; - - companion object { - fun fromCode(code: String): WcJrpcMethods? = values().firstOrNull { it.code == code } - } -} - -@JsonClass(generateAdapter = true) -data class WcSignTransaction( - @Json(name = "network") - val network: Int, - - @Json(name = "transaction") - val transaction: String, -) : WcRequestData - -@JsonClass(generateAdapter = true) -data class WcSignMessage( - @Json(name = "raw") - val raw: List, - - @Json(name = "type") - val type: WCSignType, -) : WcRequestData { - - @JsonClass(generateAdapter = false) - enum class WCSignType { - MESSAGE, PERSONAL_MESSAGE, TYPED_MESSAGE, SOLANA_MESSAGE, - } - - /** - * Raw parameters will always be the message and the address. Depending on the WCSignType, - * those parameters can be swapped as description below: - * - * - MESSAGE: `[address, data ]` - * - TYPED_MESSAGE: `[address, data]` - * - PERSONAL_MESSAGE: `[data, address]` - * - SOLANA_MESSAGE: `[publicKey (address), message]` - * - * reference: https://docs.walletconnect.org/json-rpc/ethereum#eth_signtypeddata - */ - val data - get() = when (type) { - WCSignType.PERSONAL_MESSAGE -> raw[0] - else -> raw[1] - } - - val address - get() = when (type) { - WCSignType.PERSONAL_MESSAGE -> raw[1] - else -> raw[0] - } -} - -@JsonClass(generateAdapter = true) -data class WcSignMessageData( - @Json(name = "address") - val address: String, - - @Json(name = "message") - val message: String, -) : WcRequestData - -@JsonClass(generateAdapter = true) -data class WcAddChain( - @Json(name = "chainId") - val chainId: String, -) : WcRequestData - -@JsonClass(generateAdapter = true) -data class WcEthereumTransaction( - @Json(name = "from") - val from: String, - - @Json(name = "to") - val to: String?, - - @Json(name = "nonce") - val nonce: String?, - - @Json(name = "gasPrice") - val gasPrice: String?, - - @Json(name = "maxFeePerGas") - val maxFeePerGas: String?, - - @Json(name = "maxPriorityFeePerGas") - val maxPriorityFeePerGas: String?, - - @Json(name = "gas") - val gas: String?, - - @Json(name = "gasLimit") - val gasLimit: String?, - - @Json(name = "value") - val value: String?, - - @Json(name = "data") - val data: String, -) : WcRequestData - -@JsonClass(generateAdapter = true) -data class SolanaTransactionsRequest( - @Json(name = "transactions") - val transactions: List, -) : WcRequestData - -interface WcRequestData - -data class WcCustomRequestData(val data: String) : WcRequestData - -sealed class WcRequest(open val data: WcRequestData) { - data class EthSign(override val data: WcSignMessage) : WcRequest(data) - data class EthSignTransaction(override val data: WcEthereumTransaction) : WcRequest(data) - data class EthSendTransaction(override val data: WcEthereumTransaction) : WcRequest(data) - data class BnbTrade(override val data: WcBinanceTradeOrder) : WcRequest(data) - data class BnbCancel(override val data: WcBinanceCancelOrder) : WcRequest(data) - data class BnbTransfer(override val data: WcBinanceTransferOrder) : WcRequest(data) - data class BnbTxConfirm(override val data: WcBinanceTxConfirmParam) : WcRequest(data) - data class SignTransaction(override val data: WcSignTransaction) : WcRequest(data) - data class AddChain(override val data: WcAddChain) : WcRequest(data) - data class CustomRequest(override val data: WcCustomRequestData) : WcRequest(data) - data class SolanaSignRequest(override val data: SolanaTransactionRequest) : WcRequest(data) - data class SolanaSignTransactions(override val data: SolanaTransactionsRequest) : WcRequest(data) -} - -@Singleton -internal class WcJrpcRequestsDeserializer @Inject constructor(@SdkMoshi private val moshi: Moshi) { - - @Suppress("ComplexMethod", "LongMethod") - fun deserialize(method: String, params: String): WcRequest { - val customRequest = WcRequest.CustomRequest(WcCustomRequestData(params)) - val wcMethod: WcJrpcMethods = WcJrpcMethods.fromCode(method) ?: return customRequest - - return when (wcMethod) { - WcJrpcMethods.ETH_SIGN_TRANSACTION -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcEthereumTransaction::class.java), - ).fromJsonFirstOrNull(params) ?: return customRequest - WcRequest.EthSignTransaction(data = deserializedParams) - } - WcJrpcMethods.ETH_SEND_TRANSACTION -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcEthereumTransaction::class.java), - ).fromJsonFirstOrNull(params) ?: return customRequest - WcRequest.EthSendTransaction(data = deserializedParams) - } - WcJrpcMethods.ETH_SIGN -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, String::class.java), - ).fromJsonOrNull(params) ?: return customRequest - val data = WcSignMessage( - raw = deserializedParams, - type = WcSignMessage.WCSignType.MESSAGE, - ) - WcRequest.EthSign(data = data) - } - WcJrpcMethods.ETH_PERSONAL_SIGN -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, String::class.java), - ).fromJsonOrNull(params) ?: return customRequest - val data = WcSignMessage( - raw = deserializedParams, - type = WcSignMessage.WCSignType.PERSONAL_MESSAGE, - ) - WcRequest.EthSign(data = data) - } - WcJrpcMethods.ETH_SIGN_TYPE_DATA, WcJrpcMethods.ETH_SIGN_TYPE_DATA_V4 -> { - val deserializedParams = listOf( - params.substring(params.indexOf("\"") + 1, params.indexOf("\"", startIndex = 2)), - params.substring(params.indexOfFirst { it == '{' }, params.indexOfLast { it == '}' } + 1), - ) - val data = WcSignMessage( - deserializedParams, - WcSignMessage.WCSignType.TYPED_MESSAGE, - ) - Timber.d("TypedData params: $deserializedParams") - WcRequest.EthSign(data) - } - WcJrpcMethods.BNB_SIGN -> { - return deserializeBnb(moshi, params) ?: customRequest - } - WcJrpcMethods.BNB_TRANSACTION_CONFIRM -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcBinanceTxConfirmParam::class.java), - ).fromJsonFirstOrNull(params) ?: return customRequest - WcRequest.BnbTxConfirm(data = deserializedParams) - } - WcJrpcMethods.SIGN_TRANSACTION -> { - val deserializedParams = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcSignTransaction::class.java), - ).fromJsonFirstOrNull(params) ?: return customRequest - - WcRequest.SignTransaction(data = deserializedParams) - } - WcJrpcMethods.WALLET_ADD_ETHEREUM_CHAIN -> { - val deserializedParams: WcAddChain = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcAddChain::class.java), - ).fromJsonFirstOrNull(params) ?: return customRequest - - WcRequest.AddChain(data = deserializedParams) - } - WcJrpcMethods.SOLANA_SIGN_TX -> { - val tx = moshi.adapter(SolanaTransactionRequest::class.java) - .fromJsonOrNull(params) - ?: return customRequest - - WcRequest.SolanaSignRequest(data = tx) - } - WcJrpcMethods.SOLANA_SIGN_MESSAGE -> { - val signMessage = moshi.adapter(SolanaSignMessage::class.java) - .fromJsonOrNull(params) - ?: return customRequest - val data = WcSignMessage( - raw = listOf(signMessage.publicKey, signMessage.message), - type = WcSignMessage.WCSignType.SOLANA_MESSAGE, - ) - - WcRequest.EthSign(data = data) - } - WcJrpcMethods.SOLANA_SIGN_ALL_TX -> { - val transactionsToSign = moshi.adapter(SolanaTransactionsRequest::class.java) - .fromJsonOrNull(params) ?: return customRequest - WcRequest.SolanaSignTransactions(transactionsToSign) - } - } - } - - private fun deserializeBnb(moshi: Moshi, params: String): WcRequest? { - val cancelOrder = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcBinanceCancelOrder::class.java), - ).fromJsonFirstOrNull(params) - if (cancelOrder != null) return WcRequest.BnbCancel(cancelOrder) - - val tradeOrder = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcBinanceTradeOrder::class.java), - ).fromJsonFirstOrNull(params) - if (tradeOrder != null) return WcRequest.BnbTrade(tradeOrder) - - val transferOrder = moshi.adapter>( - Types.newParameterizedType(List::class.java, WcBinanceTransferOrder::class.java), - ).fromJsonFirstOrNull(params) - if (transferOrder != null) return WcRequest.BnbTransfer(transferOrder) - - return null - } - - private fun JsonAdapter.fromJsonOrNull(data: String): T? { - return try { - this.fromJson(data) - } catch (e: Exception) { - Timber.e(e.message) - null - } - } - - private fun JsonAdapter>.fromJsonFirstOrNull(data: String): T? { - return try { - this.fromJson(data)?.firstOrNull() - } catch (e: Exception) { - Timber.e(e.message) - null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt deleted file mode 100644 index ba9f21b2d6..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcPreparedRequest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.tangem.tap.domain.walletconnect2.domain.models.BnbData -import com.tangem.tap.features.details.redux.walletconnect.WcPersonalSignData -import com.tangem.tap.features.details.redux.walletconnect.WcTransactionData - -sealed class WcPreparedRequest( - open val preparedRequestData: Any, - val topic: String, - val requestId: Long, - val derivationPath: String?, -) { - class EthSign( - override val preparedRequestData: WcPersonalSignData, - topic: String, - requestId: Long, - derivationPath: String?, - ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) - - class EthTransaction( - override val preparedRequestData: WcTransactionData, - topic: String, - requestId: Long, - derivationPath: String?, - ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) - - class BnbTransaction( - override val preparedRequestData: BnbData, - topic: String, - requestId: Long, - derivationPath: String?, - ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) - - class SolanaSignTransaction( - override val preparedRequestData: GenericTransactionData.SingleHash, - topic: String, - requestId: Long, - derivationPath: String?, - ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) - - class SolanaSignMultipleTransactions( - override val preparedRequestData: GenericTransactionData.MultipleHashes, - topic: String, - requestId: Long, - derivationPath: String?, - ) : WcPreparedRequest(preparedRequestData, topic, requestId, derivationPath) -} - -sealed interface GenericTransactionData { - val dAppName: String - val type: TransactionType - - data class SingleHash( - val hashToSign: ByteArray, - override val dAppName: String, - override val type: TransactionType, - ) : GenericTransactionData - - data class MultipleHashes( - val hashesToSign: List, - override val dAppName: String, - override val type: TransactionType, - ) : GenericTransactionData -} - -enum class TransactionType { - SOLANA_TX, -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt deleted file mode 100644 index 90de1f8b12..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt +++ /dev/null @@ -1,184 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain - -import com.tangem.blockchain.blockchains.solana.SolanaTransactionHelper -import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository -import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper -import com.tangem.tap.domain.walletconnect2.domain.models.BnbData -import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType -import okio.ByteString.Companion.decodeBase64 - -internal class WcSessionRequestConverter( - private val blockchainHelper: WcBlockchainHelper, - private val sessionsRepository: WalletConnectSessionsRepository, - private val sdkHelper: WalletConnectSdkHelper, -) { - - @Suppress("LongMethod") - suspend fun prepareRequest( - sessionRequest: WalletConnectEvents.SessionRequest, - userWalletId: String, - ): Result = runCatching { - val networkId = requireNotNull(blockchainHelper.chainIdToNetworkIdOrNull(sessionRequest.chainId.orEmpty())) { - "Failed to get network ID for chain ID: ${sessionRequest.chainId}" - } - val derivationPath = getDerivationPath( - sessionsRepository = sessionsRepository, - sessionRequest = sessionRequest, - userWalletId = userWalletId, - walletAddress = getWalletAddress(sessionRequest.request), - ) - - when (val request = sessionRequest.request) { - is WcRequest.EthSendTransaction -> { - val data = sdkHelper.prepareTransactionData( - EthTransactionData( - transaction = request.data, - networkId = networkId, - rawDerivationPath = derivationPath, - id = sessionRequest.id, - topic = sessionRequest.topic, - type = WcEthTransactionType.EthSendTransaction, - metaName = sessionRequest.metaName, - metaUrl = sessionRequest.metaUrl, - ), - ) - - WcPreparedRequest.EthTransaction( - preparedRequestData = data, - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.EthSignTransaction -> { - val data = sdkHelper.prepareTransactionData( - EthTransactionData( - transaction = request.data, - networkId = networkId, - rawDerivationPath = derivationPath, - id = sessionRequest.id, - topic = sessionRequest.topic, - type = WcEthTransactionType.EthSignTransaction, - metaName = sessionRequest.metaName, - metaUrl = sessionRequest.metaUrl, - ), - ) - - WcPreparedRequest.EthTransaction( - preparedRequestData = data, - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.EthSign -> { - val data = sdkHelper.prepareDataForPersonalSign( - request.data, - sessionRequest.topic, - sessionRequest.metaName, - sessionRequest.id, - ) - WcPreparedRequest.EthSign( - preparedRequestData = data, - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.BnbTrade -> { - val data = sdkHelper.prepareBnbTradeOrder(request.data) - WcPreparedRequest.BnbTransaction( - BnbData( - data = data, - topic = sessionRequest.topic, - requestId = sessionRequest.id, - dAppName = sessionRequest.metaName, - ), - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.BnbTransfer -> { - val data = sdkHelper.prepareBnbTransferOrder(request.data) - WcPreparedRequest.BnbTransaction( - BnbData( - data = data, - topic = sessionRequest.topic, - requestId = sessionRequest.id, - dAppName = sessionRequest.metaName, - ), - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.SolanaSignRequest -> { - val transaction = request.data.transaction - - WcPreparedRequest.SolanaSignTransaction( - preparedRequestData = GenericTransactionData.SingleHash( - hashToSign = transaction.prepareSolanaTransaction(), - dAppName = sessionRequest.metaName, - type = TransactionType.SOLANA_TX, - ), - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - is WcRequest.SolanaSignTransactions -> { - val transactions = request.data.transactions - WcPreparedRequest.SolanaSignMultipleTransactions( - preparedRequestData = GenericTransactionData.MultipleHashes( - hashesToSign = transactions.map { it.prepareSolanaTransaction() }, // - dAppName = sessionRequest.metaName, - type = TransactionType.SOLANA_TX, - ), - topic = sessionRequest.topic, - requestId = sessionRequest.id, - derivationPath = derivationPath, - ) - } - else -> throw WalletConnectError.UnsupportedMethod - } - } - - /** - * Input transaction in Base64 string - */ - private fun String.prepareSolanaTransaction(): ByteArray { - val transaction = this.decodeBase64()?.toByteArray() ?: ByteArray(0) - return SolanaTransactionHelper.removeSignaturesPlaceholders(transaction) - } - - private fun getWalletAddress(request: WcRequest): String? { - return when (request) { - is WcRequest.BnbTrade -> request.data.accountNumber - is WcRequest.BnbTransfer -> request.data.accountNumber - is WcRequest.EthSendTransaction -> request.data.from - is WcRequest.EthSignTransaction -> request.data.from - is WcRequest.EthSign -> request.data.address - is WcRequest.SolanaSignRequest -> request.data.feePayer - else -> null - } - } - - private suspend fun getDerivationPath( - sessionsRepository: WalletConnectSessionsRepository, - sessionRequest: WalletConnectEvents.SessionRequest, - userWalletId: String, - walletAddress: String?, - ): String? { - return sessionsRepository.loadSessions(userWalletId) - .firstOrNull { it.topic == sessionRequest.topic } - ?.accounts?.firstOrNull { - // if walletAddress == null take first account - it.chainId == sessionRequest.chainId && - (walletAddress == null || it.walletAddress.lowercase() == walletAddress.lowercase()) - }?.derivationPath - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/BnbData.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/BnbData.kt deleted file mode 100644 index 194ca5d01d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/BnbData.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData - -data class BnbData( - val data: BinanceMessageData, - val topic: String, - val requestId: Long, - val dAppName: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/EthTransactionData.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/EthTransactionData.kt deleted file mode 100644 index 2138d6bd63..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/EthTransactionData.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction -import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType - -data class EthTransactionData( - val transaction: WcEthereumTransaction, - val networkId: String, - val rawDerivationPath: String?, - val id: Long, - val topic: String, - val type: WcEthTransactionType, - val metaName: String, - val metaUrl: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/NetworkNamespace.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/NetworkNamespace.kt deleted file mode 100644 index a431210e42..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/NetworkNamespace.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -@JvmInline -value class NetworkNamespace(val value: String) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/RequestData.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/RequestData.kt deleted file mode 100644 index 38cd19ca97..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/RequestData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -data class RequestData( - val topic: String, - val requestId: Long, - val method: String, - val blockchain: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt deleted file mode 100644 index 40d82cdb51..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -sealed class WalletConnectError(val error: String) : Exception() { - - data object UnsupportedDApp : WalletConnectError("UnsupportedDApp") - - data class ApprovalErrorMissingNetworks( - val missingChains: List, - ) : WalletConnectError("ApprovalErrorMissingNetworks") - - data class ApprovalErrorAddNetwork( - val networks: List, - ) : WalletConnectError("ApprovalErrorAddNetwork") - - data class ApprovalErrorUnsupportedNetwork( - val unsupportedNetworks: List, - ) : WalletConnectError("ApprovalErrorUnsupportedNetwork") - - data class ExternalApprovalError( - override val message: String?, - ) : WalletConnectError("ExternalApprovalError") - - data class UnknownError( - override val message: String, - ) : WalletConnectError(message) - - data object WrongUserWallet : WalletConnectError("WrongUserWallet") - data object UnsupportedMethod : WalletConnectError("UnsupportedMethod") - data object SigningError : WalletConnectError("SigningError") - data object ValidationError : WalletConnectError("ValidationError") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectEvents.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectEvents.kt deleted file mode 100644 index 1298ede44d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectEvents.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -import com.tangem.domain.walletconnect.model.legacy.Account -import com.tangem.tap.domain.walletconnect2.domain.WcRequest -import java.net.URI - -sealed interface WalletConnectEvents { - data class SessionProposal( - val name: String, - val description: String, - val url: String, - val icons: List, - val requiredChainIds: List, - val optionalChainIds: List, - ) : WalletConnectEvents - - data class SessionApprovalError(val error: WalletConnectError) : WalletConnectEvents - data class SessionApprovalSuccess(val topic: String, val accounts: List) : WalletConnectEvents - data class SessionDeleted(val topic: String) : WalletConnectEvents - - data class SessionRequest( - val request: WcRequest, - val chainId: String?, - val topic: String, - val id: Long, - val metaName: String, - val metaUrl: String, - val method: String, - ) : WalletConnectEvents - - data class PairConnectError(val error: Throwable) : WalletConnectEvents -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectSession.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectSession.kt deleted file mode 100644 index fb535bcc66..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectSession.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -data class WalletConnectSession( - val topic: String, - val icon: String?, - val name: String?, - val url: String?, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt deleted file mode 100644 index 112c92e7a6..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.binance - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.tap.domain.walletconnect2.domain.WcRequestData - -@Suppress("LongParameterList") -@JsonClass(generateAdapter = true) -data class WcBinanceCancelOrder( - @Json(name = "account_number") - val accountNumber: String, - @Json(name = "chain_id") - val chainId: String, - @Json(name = "data") - val data: String?, - @Json(name = "memo") - val memo: String?, - @Json(name = "sequence") - val sequence: String, - @Json(name = "source") - val source: String, - @Json(name = "msgs") - val msgs: List, -) : WcRequestData { - - @JsonClass(generateAdapter = true) - data class Message( - @Json(name = "refid") - val refid: String, - @Json(name = "sender") - val sender: String, - @Json(name = "symbol") - val symbol: String, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt deleted file mode 100644 index bdeaba5b27..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.binance - -import com.github.salomonbrys.kotson.jsonSerializer -import com.google.gson.JsonObject -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.tap.domain.walletconnect2.domain.WcRequestData - -@Suppress("LongParameterList") -@JsonClass(generateAdapter = true) -data class WcBinanceTradeOrder( - @Json(name = "account_number") - val accountNumber: String, - @Json(name = "chain_id") - val chainId: String, - @Json(name = "data") - val data: String?, - @Json(name = "memo") - val memo: String?, - @Json(name = "sequence") - val sequence: String, - @Json(name = "source") - val source: String, - @Json(name = "msgs") - val msgs: List, -) : WcRequestData { - - @JsonClass(generateAdapter = false) - enum class MessageKey(val key: String) { - ID("id"), - ORDER_TYPE("ordertype"), - PRICE("price"), - QUANTITY("quantity"), - SENDER("sender"), - SIDE("side"), - SYMBOL("symbol"), - TIME_INFORCE("timeinforce"), - } - - @JsonClass(generateAdapter = true) - data class Message( - @Json(name = "id") - val id: String, - @Json(name = "orderType") - val orderType: Int, - @Json(name = "price") - val price: Long, - @Json(name = "quantity") - val quantity: Long, - @Json(name = "sender") - val sender: String, - @Json(name = "side") - val side: Int, - @Json(name = "symbol") - val symbol: String, - @Json(name = "timeInforce") - val timeInforce: Int, - ) -} - -val tradeOrderSerializer = jsonSerializer { - val jsonObject = JsonObject() - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.ID.key, it.src.id) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.ORDER_TYPE.key, it.src.orderType) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.PRICE.key, it.src.price) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.QUANTITY.key, it.src.quantity) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.SENDER.key, it.src.sender) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.SIDE.key, it.src.side) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.SYMBOL.key, it.src.symbol) - jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.TIME_INFORCE.key, it.src.timeInforce) - - jsonObject -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt deleted file mode 100644 index 99199c17dc..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.binance - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.tap.domain.walletconnect2.domain.WcRequestData - -@Suppress("LongParameterList") -@JsonClass(generateAdapter = true) -data class WcBinanceTransferOrder( - @Json(name = "account_number") - val accountNumber: String, - @Json(name = "chain_id") - val chainId: String, - @Json(name = "data") - val data: String?, - @Json(name = "memo") - val memo: String?, - @Json(name = "sequence") - val sequence: String, - @Json(name = "source") - val source: String, - @Json(name = "msgs") - val msgs: List, -) : WcRequestData { - - @JsonClass(generateAdapter = true) - data class Message( - @Json(name = "inputs") - val inputs: List, - @Json(name = "outputs") - val outputs: List, - ) { - - @JsonClass(generateAdapter = true) - data class Item( - @Json(name = "address") - val address: String, - @Json(name = "coins") - val coins: List, - ) { - - @JsonClass(generateAdapter = true) - data class Coin( - @Json(name = "amount") - val amount: Long, - @Json(name = "denom") - val denom: String, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTxConfirmParam.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTxConfirmParam.kt deleted file mode 100644 index 24ada4ca98..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTxConfirmParam.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.binance - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.tap.domain.walletconnect2.domain.WcRequestData - -@JsonClass(generateAdapter = true) -data class WcBinanceTxConfirmParam( - @Json(name = "ok") - val ok: Boolean, - @Json(name = "errorMsg") - val errorMsg: String?, -) : WcRequestData \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaSignMessage.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaSignMessage.kt deleted file mode 100644 index a521aa55e9..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaSignMessage.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.solana - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -internal data class SolanaSignMessage( - @Json(name = "pubkey") - val publicKey: String, - - @Json(name = "message") - val message: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt deleted file mode 100644 index bed9fd3897..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.solana - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.tap.domain.walletconnect2.domain.WcRequestData - -@JsonClass(generateAdapter = true) -data class SolanaTransactionRequest( - @Json(name = "feePayer") - val feePayer: String?, - - @Json(name = "transaction") - val transaction: String, -) : WcRequestData \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/toggles/DefaultWalletConnectFeatureToggles.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/toggles/DefaultWalletConnectFeatureToggles.kt deleted file mode 100644 index 03cb9b878d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/toggles/DefaultWalletConnectFeatureToggles.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.toggles - -import com.tangem.core.configtoggle.feature.FeatureTogglesManager -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles - -internal class DefaultWalletConnectFeatureToggles( - private val featureTogglesManager: FeatureTogglesManager, -) : WalletConnectFeatureToggles { - - override val isRedesignedWalletConnectEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled("WALLET_CONNECT_REDESIGN_ENABLED") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt index 0ba699bde1..e4070d4b60 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -1,44 +1,22 @@ package com.tangem.tap.features.demo -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.message.DialogMessage import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.models.scan.ScanResponse -import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store -import com.tangem.wallet.R -import org.rekotlin.Action object DemoHelper { val config = DemoConfig() - private val disabledActionFeatures = listOf( - WalletConnectAction.StartWalletConnect::class.java, - ) - fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId) fun isTestDemoCard(scanResponse: ScanResponse): Boolean = config.isTestDemoCardId(scanResponse.card.cardId) fun isDemoCardId(cardId: String): Boolean = config.isDemoCardId(cardId) - fun tryHandle(appState: () -> AppState?, action: Action): Boolean { + fun tryHandle(appState: () -> AppState?): Boolean { val scanResponse = getScanResponse(appState) ?: return false if (!scanResponse.isDemoCard()) return false - disabledActionFeatures.firstOrNull { it == action::class.java }?.let { - val uiMessageSender = store.inject(DaggerGraphState::uiMessageSender) - uiMessageSender.send( - DialogMessage( - message = resourceReference(R.string.alert_demo_feature_disabled), - ), - ) - return true - } - return false } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 9281cbc507..2ebe0494ab 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -39,7 +39,7 @@ class DetailsMiddleware { val detailsMiddleware: Middleware = { _, stateProvider -> { next -> { action -> - if (!DemoHelper.tryHandle(stateProvider, action)) { + if (!DemoHelper.tryHandle(stateProvider)) { val detailsState = stateProvider()?.detailsState if (detailsState != null) { handleAction(detailsState, action) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt deleted file mode 100644 index bc90c602d0..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tangem.tap.features.details.redux.walletconnect - -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen -import org.rekotlin.Action - -sealed class WalletConnectAction : Action { - data class HandleDeepLink(val wcUri: String?) : WalletConnectAction() - - data object StartWalletConnect : WalletConnectAction() - - data class OpenSession( - val wcUri: String, - val source: SourceType, - val userWalletId: UserWalletId, - ) : WalletConnectAction() { - enum class SourceType { QR, DEEPLINK, CLIPBOARD, ETC } - } - - data class DisconnectSession(val topic: String) : WalletConnectAction() - - data class RejectRequest(val topic: String, val id: Long) : WalletConnectAction() - - //region WalletConnect 2.0 - data class ApproveProposal(val proposal: WalletConnectEvents.SessionProposal) : WalletConnectAction() - data object RejectProposal : WalletConnectAction() - - data object SessionEstablished : WalletConnectAction() - data class SessionRejected(val error: WalletConnectError) : WalletConnectAction() - data class SessionListUpdated(val sessions: List) : WalletConnectAction() - - data class ShowSessionRequest(val sessionRequest: WcPreparedRequest) : WalletConnectAction() - - data object RejectUnsupportedRequest : WalletConnectAction() - - data class PerformRequestedAction(val sessionRequest: WcPreparedRequest) : WalletConnectAction() - - data class PairConnectErrorAction(val throwable: Throwable) : WalletConnectAction() - - data object UnsupportedDappRequest : WalletConnectAction() - //endregion WalletConnect 2.0 -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt deleted file mode 100644 index 499e1861b0..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.tangem.tap.features.details.redux.walletconnect - -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.common.routing.AppRoute -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.coroutines.launch -import org.rekotlin.Action -import org.rekotlin.Middleware -import timber.log.Timber - -@Suppress("LargeClass") -class WalletConnectMiddleware { - private val walletConnectInteractor: WalletConnectInteractor - get() = store.inject(DaggerGraphState::walletConnectInteractor) - private val walletConnectRepository: LegacyWalletConnectRepository - get() = store.inject(DaggerGraphState::walletConnectRepository) - - val walletConnectMiddleware: Middleware = { dispatch, state -> - { next -> - { action -> - handle(state, action) - next(action) - } - } - } - - @Suppress("ComplexMethod", "LongMethod") - private fun handle(state: () -> AppState?, action: Action) { - if (DemoHelper.tryHandle(state, action)) return - - when (action) { - is WalletConnectAction.HandleDeepLink -> { - val wsUrl = action.wcUri - Timber.i("WC deeplink: $wsUrl") - if (!wsUrl.isNullOrBlank()) { - Timber.i("WC deeplink added to stack: $wsUrl") - walletConnectInteractor.addDeeplink(wsUrl) - } - } - is WalletConnectAction.DisconnectSession -> { - walletConnectInteractor.disconnectSession(action.topic) - } - is WalletConnectAction.StartWalletConnect -> { - store.dispatchNavigationAction { - push(AppRoute.QrScanning(AppRoute.QrScanning.Source.WalletConnect)) - } - } - is WalletConnectAction.OpenSession -> { - val index = action.wcUri.indexOf("@") - when (action.wcUri[index + 1]) { - '2' -> walletConnectRepository.pair( - uri = action.wcUri, - source = action.source, - userWalletId = action.userWalletId, - ) - '1' -> { - store.dispatchOnMain(WalletConnectAction.UnsupportedDappRequest) - store.dispatchOnMain( - GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedWcVersion), - ) - } - } - } - is WalletConnectAction.RejectRequest -> { - walletConnectInteractor.cancelRequest(action.topic, action.id) - } - is WalletConnectAction.ApproveProposal -> { - scope.launch { - val proposalChainIds = action.proposal.requiredChainIds + action.proposal.optionalChainIds - val proposalBlockchains = - walletConnectInteractor.blockchainHelper.chainIdsToBlockchains(proposalChainIds) - val accounts = getWalletManagers() - .filter { walletManager -> proposalBlockchains.contains(walletManager.wallet.blockchain) } - .flatMap { - val wallet = it.wallet - val chainIds = walletConnectInteractor.blockchainHelper.networkIdToChainIdOrNull( - wallet.blockchain.toNetworkId(), - ) - walletConnectInteractor.blockchainHelper.chainIdsToAccounts( - walletAddress = wallet.address, - chainIds = chainIds, - derivationPath = wallet.publicKey.derivationPath?.rawPath, - ) - } - walletConnectInteractor.approveSessionProposal(accounts) - } - } - is WalletConnectAction.RejectProposal -> { - walletConnectInteractor.rejectSessionProposal() - } - is WalletConnectAction.SessionEstablished -> { - } - is WalletConnectAction.SessionRejected -> { - when (action.error) { - is WalletConnectError.ApprovalErrorAddNetwork -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.AddNetwork(action.error.networks), - ), - ) - } - is WalletConnectError.ApprovalErrorUnsupportedNetwork -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.UnsupportedNetwork(action.error.unsupportedNetworks), - ), - ) - } - is WalletConnectError.UnsupportedDApp -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.UnsupportedDapp, - ), - ) - } - is WalletConnectError.UnknownError -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - AppDialog.SimpleOkDialogRes( - headerId = R.string.wallet_connect_title, - messageId = R.string.wallet_connect_error_with_framework_message, - args = listOf(action.error.message), - ), - ), - ) - } - is WalletConnectError.ExternalApprovalError -> { - Timber.e(action.error, "ExternalApprovalError ${action.error.message}") - // do not show dialog on this event - // val message = action.error.message - // if (!message.isNullOrEmpty()) { - // store.dispatchOnMain( - // GlobalAction.ShowDialog( - // AppDialog.SimpleOkWarningDialog( - // message = message, - // ), - // ), - // ) - // } - } - else -> Unit - } - } - - is WalletConnectAction.ShowSessionRequest -> { - val dialog: WalletConnectDialog = when (val request = action.sessionRequest) { - is WcPreparedRequest.BnbTransaction -> WalletConnectDialog.BnbTransactionDialog(request) - is WcPreparedRequest.EthTransaction -> WalletConnectDialog.RequestTransaction(request) - is WcPreparedRequest.EthSign -> WalletConnectDialog.PersonalSign(request) - is WcPreparedRequest.SolanaSignTransaction -> - WalletConnectDialog.SignTransactionDialog(request) - is WcPreparedRequest.SolanaSignMultipleTransactions -> - WalletConnectDialog.SignTransactionsDialog(request) - } - store.dispatch(GlobalAction.ShowDialog(dialog)) - } - is WalletConnectAction.PerformRequestedAction -> { - scope.launch { walletConnectInteractor.continueWithRequest(action.sessionRequest) } - } - is WalletConnectAction.RejectUnsupportedRequest -> { - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork())) - } - is WalletConnectAction.PairConnectErrorAction -> { - store.dispatch(GlobalAction.ShowDialog(WalletConnectDialog.PairConnectErrorDialog(action.throwable))) - } - } - } - - private suspend fun getWalletManagers(): List { - val walletManagerFacade = store.inject(DaggerGraphState::walletManagersFacade) - val userWallet = getSelectedWallet() ?: return emptyList() - return walletManagerFacade.getStoredWalletManagers(userWallet.walletId) - } - - private suspend fun getSelectedWallet(): UserWallet? { - val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) - val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository) - val hotWalletFeatureToggles = store.inject(DaggerGraphState::hotWalletFeatureToggles) - - return if (hotWalletFeatureToggles.isHotWalletEnabled) { - userWalletsListRepository.selectedUserWalletSync() - } else { - userWalletsListManager.selectedUserWalletSync - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt deleted file mode 100644 index 1cf6c4da24..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.tap.features.details.redux.walletconnect - -import org.rekotlin.Action - -object WalletConnectReducer { - fun reduce(action: Action, state: WalletConnectState): WalletConnectState { - if (action !is WalletConnectAction) return state - - return when (action) { - is WalletConnectAction.OpenSession -> { - state.copy(loading = true) - } - is WalletConnectAction.ApproveProposal -> state.copy(loading = true) - is WalletConnectAction.RejectProposal, - is WalletConnectAction.SessionEstablished, - is WalletConnectAction.SessionRejected, - is WalletConnectAction.PairConnectErrorAction, - is WalletConnectAction.UnsupportedDappRequest, - -> state.copy(loading = false) - is WalletConnectAction.SessionListUpdated -> state.copy( - wc2Sessions = action.sessions, - ) - else -> state - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt deleted file mode 100644 index ac28ca0b95..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ /dev/null @@ -1,173 +0,0 @@ -package com.tangem.tap.features.details.redux.walletconnect - -import com.squareup.moshi.JsonClass -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.TransactionData -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.redux.StateDialog -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen -import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData -import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData - -data class WalletConnectState( - val loading: Boolean = false, - val wc2Sessions: List = listOf(), - val newSessionData: NewWcSessionData? = null, -) - -data class NewWcSessionData( - val session: WalletConnectSession, - val scanResponse: ScanResponse, - val blockchain: Blockchain?, -) - -data class WalletConnectSession( - val peerId: String, - val remotePeerId: String?, - val wallet: WalletForSession, -) { - fun getAddress(): String? { - val key = wallet.derivedPublicKey ?: wallet.walletPublicKey ?: return null - return wallet.blockchain?.makeAddresses(key)?.first()?.value - } -} - -@JsonClass(generateAdapter = true) -data class WalletForSession( - val walletPublicKey: ByteArray? = null, - val derivedPublicKey: ByteArray? = null, - val derivationPath: DerivationPath? = null, - val derivationStyle: DerivationStyle? = null, - val isTestNet: Boolean = false, - val blockchain: Blockchain? = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum, -) { - - fun getBlockchainForSession(): Blockchain { - return blockchain ?: if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as WalletForSession - - if (walletPublicKey != null) { - if (other.walletPublicKey == null) return false - if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false - } else if (other.walletPublicKey != null) return false - if (derivedPublicKey != null) { - if (other.derivedPublicKey == null) return false - if (!derivedPublicKey.contentEquals(other.derivedPublicKey)) return false - } else if (other.derivedPublicKey != null) return false - if (derivationPath != other.derivationPath) return false - if (isTestNet != other.isTestNet) return false - return blockchain == other.blockchain - } - - override fun hashCode(): Int { - var result = walletPublicKey?.contentHashCode() ?: 0 - result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0) - result = 31 * result + (derivationPath?.hashCode() ?: 0) - result = 31 * result + isTestNet.hashCode() - result = 31 * result + (blockchain?.hashCode() ?: 0) - return result - } -} - -sealed class WalletConnectDialog : StateDialog { - data object UnsupportedWcVersion : WalletConnectDialog() - data object UnsupportedCard : WalletConnectDialog() - data class UnsupportedNetwork(val networks: List? = null) : WalletConnectDialog() - data object UnsupportedDapp : WalletConnectDialog() - data class AddNetwork(val networks: List) : WalletConnectDialog() - data object OpeningSessionRejected : WalletConnectDialog() - data object SessionTimeout : WalletConnectDialog() - data class ApproveWcSession( - val session: WalletConnectSession, - val networks: List, - ) : WalletConnectDialog() - - data class SessionProposalDialog( - val sessionProposal: WalletConnectEvents.SessionProposal, - val networks: String, - val onApprove: () -> Unit, - val onReject: () -> Unit, - ) : WalletConnectDialog() - - data class ChooseNetwork( - val session: WalletConnectSession, - val networks: List, - ) : WalletConnectDialog() - - data class RequestTransaction(val data: WcPreparedRequest.EthTransaction) : - WalletConnectDialog() - - data class PersonalSign(val data: WcPreparedRequest.EthSign) : WalletConnectDialog() - data class BnbTransactionDialog( - val data: WcPreparedRequest.BnbTransaction, - ) : WalletConnectDialog() - - data class SignTransactionDialog( - val data: WcPreparedRequest.SolanaSignTransaction, - ) : WalletConnectDialog() - - data class SignTransactionsDialog( - val data: WcPreparedRequest.SolanaSignMultipleTransactions, - ) : WalletConnectDialog() - - data class PairConnectErrorDialog(val error: Throwable) : WalletConnectDialog() -} - -data class WcTransactionData( - val type: WcEthTransactionType, - val transaction: TransactionData, - val topic: String, - val id: Long, - val walletManager: WalletManager, - val dialogData: TransactionRequestDialogData, -) - -enum class WcEthTransactionType { - EthSignTransaction, - EthSendTransaction, -} - -data class WcPersonalSignData( - val hash: ByteArray, - val topic: String, - val id: Long, - val dialogData: PersonalSignDialogData, - val type: WcSignMessage.WCSignType, -) - -sealed class BinanceMessageData( - val address: String, - val data: ByteArray, -) { - class Trade( - val tradeData: List, - address: String, - data: ByteArray, - ) : BinanceMessageData(address, data) - - class Transfer( - val outputAddress: String, - val amount: String, - address: String, - data: ByteArray, - ) : BinanceMessageData(address, data) -} - -data class TradeData( - val price: String, - val quantity: String, - val amount: String, - val symbol: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt deleted file mode 100644 index 0974e0db1b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier -import com.arkivanov.essenty.lifecycle.subscribe -import com.tangem.common.routing.AppRouter -import com.tangem.core.analytics.Analytics -import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.tap.common.analytics.events.WalletConnect -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState -import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent -import com.tangem.tap.store -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import org.rekotlin.StoreSubscriber - -@Suppress("UnusedPrivateMember") -internal class DefaultWalletConnectComponent @AssistedInject constructor( - @Assisted appComponentContext: AppComponentContext, - @Assisted params: WalletConnectComponent.Params, -) : WalletConnectComponent, AppComponentContext by appComponentContext, StoreSubscriber { - - private val model: WalletConnectModel = getOrCreateModel(params) - - private var screenState: MutableState = - mutableStateOf(model.updateState(store.state.walletConnectState)) - - init { - lifecycle.subscribe( - onCreate = { - Analytics.send(WalletConnect.ScreenOpened()) - }, - onStart = { - store.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.walletConnectState == newState.walletConnectState - }.select { it.walletConnectState } - } - }, - onStop = { - store.unsubscribe(this) - }, - ) - } - - override fun newState(state: WalletConnectState) { - screenState.value = model.updateState(state) - } - - @Composable - override fun Content(modifier: Modifier) { - WalletConnectScreen( - modifier = modifier, - state = screenState.value, - onBackClick = { - store.dispatchNavigationAction(AppRouter::pop) - }, - ) - } - - @AssistedFactory - interface Factory : WalletConnectComponent.Factory { - override fun create( - context: AppComponentContext, - params: WalletConnectComponent.Params, - ): DefaultWalletConnectComponent - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt deleted file mode 100644 index a231426ebb..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import androidx.compose.runtime.Stable -import arrow.core.getOrElse -import com.tangem.core.decompose.di.ModelScoped -import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.domain.qrscanning.models.QrResultSource -import com.tangem.domain.qrscanning.models.SourceType -import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState -import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -@Stable -@ModelScoped -internal class WalletConnectModel @Inject constructor( - override val dispatchers: CoroutineDispatcherProvider, - private val listenToQrScanningUseCase: ListenToQrScanningUseCase, - paramsContainer: ParamsContainer, -) : Model() { - - private val params = paramsContainer.require() - - init { - modelScope.launch { - listenToQrScanningUseCase.listen(SourceType.WALLET_CONNECT) - .getOrElse { emptyFlow() } - .map { result -> - val source = when (result.resultSource) { - QrResultSource.CLIPBOARD -> WalletConnectAction.OpenSession.SourceType.CLIPBOARD - QrResultSource.CAMERA, - QrResultSource.GALLERY, - -> WalletConnectAction.OpenSession.SourceType.QR - } - WalletConnectAction.OpenSession( - wcUri = result.qrCode, - source = source, - userWalletId = params.userWalletId, - ) - } - .collect { store.dispatch(it) } - } - } - - fun updateState(state: WalletConnectState): WalletConnectScreenState { - Timber.d("WC2 Sessions: ${state.wc2Sessions}") - val sessions = state.wc2Sessions - return WalletConnectScreenState( - sessions.toImmutableList(), - isLoading = state.loading, - onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) }, - onAddSession = { - store.dispatch(WalletConnectAction.StartWalletConnect) - }, - ) - } - - private fun onRemoveSession(sessionUri: String, wc2sessions: List) { - wc2sessions.firstOrNull { it.sessionId == sessionUri }?.let { - store.dispatch(WalletConnectAction.DisconnectSession(sessionUri)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt deleted file mode 100644 index 252fc23b51..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreen.kt +++ /dev/null @@ -1,169 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.tangem.core.analytics.Analytics -import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.common.analytics.events.Settings -import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold -import com.tangem.wallet.R -import kotlinx.collections.immutable.persistentListOf - -@Composable -internal fun WalletConnectScreen( - state: WalletConnectScreenState, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - SettingsScreensScaffold( - modifier = modifier, - content = { - if (state.sessions.isEmpty()) { - EmptyScreen(state) - } else { - WalletConnectSessions(state) - } - }, - fab = { - if (!state.isLoading) { - AddSessionFab( - onAddSession = { - Analytics.send(Settings.ButtonStartWalletConnectSession()) - state.onAddSession() - }, - ) - } - }, - titleRes = R.string.wallet_connect_title, - onBackClick = onBackClick, - ) -} - -@Composable -private fun AddSessionFab(onAddSession: () -> Unit, modifier: Modifier = Modifier) { - FloatingActionButton( - onClick = onAddSession, - containerColor = TangemTheme.colors.button.primary, - contentColor = TangemTheme.colors.icon.primary2, - shape = RoundedCornerShape(16.dp), - modifier = modifier, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_plus_24), - contentDescription = "", - ) - } -} - -@Composable -private fun EmptyScreen(state: WalletConnectScreenState) { - if (state.isLoading) { - TangemLinearProgressIndicator( - modifier = Modifier.fillMaxWidth(), - color = TangemTheme.colors.icon.accent, - ) - } - Column( - modifier = Modifier - .fillMaxSize() - .padding(bottom = 64.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Image( - painter = painterResource(id = R.drawable.ic_wallet_connect_24), - contentDescription = "", - colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive), - contentScale = ContentScale.FillWidth, - modifier = Modifier.width(width = 100.dp), - ) - Spacer(modifier = Modifier.size(24.dp)) - Text( - text = stringResourceSafe(id = R.string.wallet_connect_subtitle), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.tertiary, - ) - } -} - -@Composable -private fun WalletConnectSessions(state: WalletConnectScreenState) { - if (state.isLoading) { - TangemLinearProgressIndicator( - modifier = Modifier - .fillMaxWidth() - .height(2.dp), - color = TangemTheme.colors.icon.accent, - ) - } else { - Spacer(modifier = Modifier.height(2.dp)) - } - - LazyColumn( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - items(state.sessions) { session -> - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 16.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = session.description, - style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.primary1, - modifier = Modifier.weight(1f), - ) - IconButton( - onClick = { - Analytics.send(Settings.ButtonStopWalletConnectSession()) - state.onRemoveSession(session.sessionId) - }, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_cross_rounded_24), - contentDescription = "", - tint = TangemTheme.colors.icon.warning, - ) - } - } - } - } -} - -@Composable -@Preview -private fun WalletConnectScreenPreview() { - WalletConnectScreen( - state = WalletConnectScreenState( - sessions = persistentListOf( - WcSessionForScreen( - description = "session from some dApp", - sessionId = "", - ), - ), - isLoading = true, - ), - {}, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreenState.kt deleted file mode 100644 index 0a3d429cd6..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectScreenState.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import kotlinx.collections.immutable.ImmutableList - -internal data class WalletConnectScreenState( - val sessions: ImmutableList, - val isLoading: Boolean = false, - val onRemoveSession: (String) -> Unit = {}, - val onAddSession: () -> Unit = {}, -) - -data class WcSessionForScreen( - val description: String, - val sessionId: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt deleted file mode 100644 index 2c97e35722..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.api - -import com.tangem.core.decompose.factory.ComponentFactory -import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.domain.models.wallet.UserWalletId - -interface WalletConnectComponent : ComposableContentComponent { - data class Params(val userWalletId: UserWalletId) - interface Factory : ComponentFactory -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt deleted file mode 100644 index 20a85d18be..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.di - -import com.tangem.core.decompose.model.Model -import com.tangem.tap.features.details.ui.walletconnect.DefaultWalletConnectComponent -import com.tangem.tap.features.details.ui.walletconnect.WalletConnectModel -import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -@Module -@InstallIn(SingletonComponent::class) -internal interface WalletConnectFeatureModule { - - @Binds - fun bindFactory(impl: DefaultWalletConnectComponent.Factory): WalletConnectComponent.Factory - - @Binds - @IntoMap - @ClassKey(WalletConnectModel::class) - fun bindModel(model: WalletConnectModel): Model -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt deleted file mode 100644 index aee88834e4..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/BnbTransactionDialog.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.store -import com.tangem.wallet.R - -object BnbTransactionDialog { - fun create(preparedData: WcPreparedRequest.BnbTransaction, context: Context): AlertDialog { - val data = preparedData.preparedRequestData.data - val message = when (data) { - is BinanceMessageData.Trade -> data.tradeData.map { - context.getString( - R.string.wallet_connect_bnb_trade_order_message, - it.symbol, - it.price, - it.quantity, - it.amount, - ) - }.joinToString(separator = "\n\n") - is BinanceMessageData.Transfer -> context.getString( - R.string.wallet_connect_bnb_transaction_message, - data.address, - data.outputAddress, - data.amount, - ) - } - - val fullMessage = context.getString( - R.string.wallet_connect_bnb_sign_message, - preparedData.preparedRequestData.dAppName, - message, - ) - val positiveButtonTitle = context.getText(R.string.common_sign) - - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(fullMessage) - setPositiveButton(positiveButtonTitle) { _, _ -> - store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(WalletConnectAction.RejectRequest(preparedData.topic, preparedData.requestId)) - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt deleted file mode 100644 index 4403b6d4a6..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.store -import com.tangem.wallet.R - -object PersonalSignDialog { - fun create(preparedData: WcPreparedRequest.EthSign, context: Context): AlertDialog { - val data = preparedData.preparedRequestData.dialogData - val message = context.getString(R.string.wallet_connect_alert_sign_message, data.message) - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(message) - setPositiveButton(context.getText(R.string.common_sign)) { _, _ -> - store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(WalletConnectAction.RejectRequest(data.topic, data.id)) - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} - -data class PersonalSignDialogData( - val dAppName: String, - val message: String, - val topic: String, - val id: Long, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SessionProposalDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SessionProposalDialog.kt deleted file mode 100644 index 1dd8aff5a7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SessionProposalDialog.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents -import com.tangem.tap.store -import com.tangem.wallet.R - -object SessionProposalDialog { - fun create( - sessionProposal: WalletConnectEvents.SessionProposal, - networks: String, - context: Context, - onApprove: () -> Unit, - onReject: () -> Unit, - ): AlertDialog { - val message = context.getString( - R.string.wallet_connect_request_session_start, - sessionProposal.name, - networks, - sessionProposal.url, - ) - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(message) - setPositiveButton(context.getText(R.string.common_start)) { _, _ -> - store.dispatch(GlobalAction.HideDialog) - onApprove() - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(GlobalAction.HideDialog) - onReject() - } - setOnCancelListener { - store.dispatch(GlobalAction.HideDialog) - onReject() - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt deleted file mode 100644 index e459b6065c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.store -import com.tangem.wallet.R - -internal object SignTransactionDialog { - - fun create(preparedData: WcPreparedRequest.SolanaSignTransaction, context: Context): AlertDialog { - val signMessage = context.getString(R.string.wallet_connect_alert_sign_message, "") - val message = "${preparedData.preparedRequestData.dAppName}\n$signMessage" - return AlertDialog.Builder(context).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(message) - setPositiveButton(context.getText(R.string.common_sign)) { _, _ -> - store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(WalletConnectAction.RejectRequest(preparedData.topic, preparedData.requestId)) - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt deleted file mode 100644 index 579940d0ce..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionsDialog.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.store -import com.tangem.wallet.R - -internal object SignTransactionsDialog { - - fun create(preparedData: WcPreparedRequest.SolanaSignMultipleTransactions, context: Context): AlertDialog { - val signMessage = context.getString(R.string.wallet_connect_alert_sign_message, "") - val message = "${preparedData.preparedRequestData.dAppName}\n$signMessage" - return AlertDialog.Builder(context).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(message) - setPositiveButton(context.getText(R.string.common_sign)) { _, _ -> - store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(WalletConnectAction.RejectRequest(preparedData.topic, preparedData.requestId)) - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt deleted file mode 100644 index 4fd49641fb..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType -import com.tangem.tap.store -import com.tangem.wallet.R - -object TransactionDialog { - fun create(preparedData: WcPreparedRequest.EthTransaction, context: Context): AlertDialog { - val data = preparedData.preparedRequestData.dialogData - val message = context.getString( - R.string.wallet_connect_create_tx_message, - data.dAppName, - data.dAppUrl, - data.amount, - data.feeAmount, - data.totalAmount, - data.balance, - ) - - val positiveButtonTitle = when (data.type) { - WcEthTransactionType.EthSignTransaction -> context.getText(R.string.common_sign) - WcEthTransactionType.EthSendTransaction -> context.getText(R.string.common_sign_and_send) - } - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(R.string.wallet_connect_title)) - setMessage(message) - setPositiveButton(positiveButtonTitle) { _, _ -> - if (data.isEnoughFundsToSend) { - store.dispatch(WalletConnectAction.PerformRequestedAction(preparedData)) - } else { - store.dispatch(WalletConnectAction.RejectRequest(data.topic, data.id)) - } - } - setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> - store.dispatch(WalletConnectAction.RejectRequest(data.topic, data.id)) - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} - -data class TransactionRequestDialogData( - val dAppName: String, - val dAppUrl: String, - val amount: String, - val feeAmount: String, - val totalAmount: String, - val balance: String, - val isEnoughFundsToSend: Boolean, - val topic: String, - val id: Long, - val type: WcEthTransactionType, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt deleted file mode 100644 index e715290a1e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.tangem.tap.features.intentHandler.handlers - -import android.content.Intent -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.removePrefixOrNull -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.intentHandler.AffectsNavigation -import com.tangem.tap.features.intentHandler.IntentHandler -import com.tangem.tap.store -import timber.log.Timber -import java.net.URLDecoder - -/** -[REDACTED_AUTHOR] - */ -class WalletConnectLinkIntentHandler : IntentHandler, AffectsNavigation { - - override fun handleIntent(intent: Intent?, isFromForeground: Boolean): Boolean { - val intentData = intent?.data ?: return false - val scheme = intent.scheme ?: return false - - val wcUri = when (scheme) { - WC_SCHEME -> intentData.toString() - TANGEM_SCHEME -> intentData.toString().removePrefixOrNull(TANGEM_WC_PREFIX) - else -> null - } - - return if (wcUri.isNullOrBlank()) { - false - } else { - val decodedWcUri = try { - URLDecoder.decode(wcUri, DEFAULT_CHARSET_NAME) - } catch (e: Exception) { - Timber.e(e) - return false - } - store.dispatchOnMain(WalletConnectAction.HandleDeepLink(decodedWcUri)) - true - } - } - - private companion object { - const val TANGEM_SCHEME = "tangem" - const val TANGEM_WC_PREFIX = "tangem://wc?uri=" - const val DEFAULT_CHARSET_NAME = "UTF-8" - const val WC_SCHEME = "wc" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt index c3567c8f3a..546cf5fd21 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt @@ -2,12 +2,15 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics -import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.Onboarding.* -import com.tangem.tap.common.extensions.* +import com.tangem.tap.backupService +import com.tangem.tap.common.analytics.events.Onboarding.Finished +import com.tangem.tap.common.extensions.dispatchNavigationAction +import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.demo.DemoHelper +import com.tangem.tap.mainScope import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.store import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -24,7 +27,7 @@ class BackupMiddleware { @Suppress("LongMethod", "ComplexMethod", "MagicNumber") private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) { - if (DemoHelper.tryHandle(appState, action)) return + if (DemoHelper.tryHandle(appState)) return when (action) { is BackupAction.DiscardBackup -> { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index d2f1baf710..2aa7a3ef24 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -30,7 +30,7 @@ object TradeCryptoMiddleware { } private fun handle(state: () -> AppState?, action: TradeCryptoAction) { - if (DemoHelper.tryHandle(state, action)) return + if (DemoHelper.tryHandle(state)) return when (action) { is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 8a842072d8..d107bc7422 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -20,7 +20,6 @@ import com.tangem.domain.wallets.legacy.unlockIfLockable import com.tangem.tap.* import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.proxy.redux.DaggerGraphState import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -32,21 +31,19 @@ internal class WelcomeMiddleware { { action -> val appState = appStateProvider() if (action is WelcomeAction && appState != null) { - handleAction(action, appState.welcomeState) + handleAction(action) } next(action) } } } - private fun handleAction(action: WelcomeAction, state: WelcomeState) { + private fun handleAction(action: WelcomeAction) { mainScope.launch { when (action) { is WelcomeAction.ProceedWithIntent -> proceedWithIntent(action.intent) - is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics( - afterUnlockIntent = action.afterUnlockIntent ?: state.intent, - ) - is WelcomeAction.ProceedWithCard -> proceedWithCard(afterScanIntent = state.intent) + is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics() + is WelcomeAction.ProceedWithCard -> proceedWithCard() is WelcomeAction.ClearUserWallets -> disableUserWalletsSaving() else -> Unit } @@ -70,13 +67,8 @@ internal class WelcomeMiddleware { } } - private suspend fun proceedWithBiometrics(afterUnlockIntent: Intent?) { - Timber.d( - """ - Proceeding with biometry - |- Intent: $afterUnlockIntent - """.trimIndent(), - ) + private suspend fun proceedWithBiometrics() { + Timber.d("Proceeding with biometry") val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) userWalletsListManager.unlockIfLockable(type = UnlockType.ANY) @@ -93,20 +85,11 @@ internal class WelcomeMiddleware { store.dispatchNavigationAction { replaceAll(AppRoute.Wallet) } store.dispatchWithMain(WelcomeAction.ProceedWithBiometrics.Success) store.onUserWalletSelected(userWallet = selectedUserWallet) - - afterUnlockIntent?.let { - WalletConnectLinkIntentHandler().handleIntent(it, false) - } } } - private suspend fun proceedWithCard(afterScanIntent: Intent?) { - Timber.d( - """ - Proceeding with card - |- Intent: $afterScanIntent - """.trimIndent(), - ) + private suspend fun proceedWithCard() { + Timber.d("Proceeding with card") scanCardInternal { scanResponse -> val userWalletBuilder = store.inject(DaggerGraphState::coldUserWalletBuilderFactory).create(scanResponse) @@ -125,10 +108,6 @@ internal class WelcomeMiddleware { store.dispatchNavigationAction { replaceAll(AppRoute.Wallet) } store.dispatchWithMain(WelcomeAction.ProceedWithCard.Success) store.onUserWalletSelected(userWallet = userWallet) - - afterScanIntent?.let { - WalletConnectLinkIntentHandler().handleIntent(it, false) - } } } } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 86116d6849..ef0fab7352 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -2,14 +2,12 @@ package com.tangem.tap.proxy.redux import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import org.rekotlin.Action sealed interface DaggerGraphAction : Action { data class SetActivityDependencies( val scanCardUseCase: ScanCardUseCase, - val walletConnectInteractor: WalletConnectInteractor, val cardSdkConfigRepository: CardSdkConfigRepository, ) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 76802dd20d..6a2e43b96a 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -14,7 +14,6 @@ object DaggerGraphReducer { return when (action) { is DaggerGraphAction.SetActivityDependencies -> state.daggerGraphState.copy( scanCardUseCase = action.scanCardUseCase, - walletConnectInteractor = action.walletConnectInteractor, cardSdkConfigRepository = action.cardSdkConfigRepository, ) } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index f7e6c83846..ef68891955 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -37,8 +37,6 @@ import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.hot.sdk.TangemHotSdk import com.tangem.operations.attestation.CardArtworksProvider import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles -import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.proxy.AppStateHolder import org.rekotlin.StateType @@ -46,8 +44,6 @@ data class DaggerGraphState( val networkConnectionManager: NetworkConnectionManager? = null, val cardScanningFeatureToggles: CardScanningFeatureToggles? = null, val scanCardUseCase: ScanCardUseCase? = null, - val walletConnectRepository: LegacyWalletConnectRepository? = null, - val walletConnectInteractor: WalletConnectInteractor? = null, val scanCardProcessor: ScanCardProcessor? = null, val cardSdkConfigRepository: CardSdkConfigRepository? = null, val appCurrencyRepository: AppCurrencyRepository? = null, diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 653b45d4c1..29a693971b 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -8,22 +8,14 @@ import com.tangem.feature.referral.api.ReferralComponent import com.tangem.feature.stories.api.StoriesComponent import com.tangem.feature.usedesk.api.UsedeskComponent import com.tangem.feature.walletsettings.component.WalletSettingsComponent -import com.tangem.features.account.ArchivedAccountListComponent import com.tangem.features.account.AccountCreateEditComponent import com.tangem.features.account.AccountDetailsComponent +import com.tangem.features.account.ArchivedAccountListComponent import com.tangem.features.createwalletselection.CreateWalletSelectionComponent import com.tangem.features.details.component.DetailsComponent import com.tangem.features.disclaimer.api.components.DisclaimerComponent import com.tangem.features.home.api.HomeComponent -import com.tangem.features.hotwallet.AddExistingWalletComponent -import com.tangem.features.hotwallet.CreateMobileWalletComponent -import com.tangem.features.hotwallet.UpgradeWalletComponent -import com.tangem.features.hotwallet.WalletActivationComponent -import com.tangem.features.hotwallet.CreateWalletBackupComponent -import com.tangem.features.hotwallet.UpdateAccessCodeComponent -import com.tangem.features.hotwallet.HotWalletFeatureToggles -import com.tangem.features.hotwallet.WalletBackupComponent -import com.tangem.features.hotwallet.ViewPhraseComponent +import com.tangem.features.hotwallet.* import com.tangem.features.managetokens.component.ChooseManagedTokensComponent import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.component.ManageTokensSource @@ -45,14 +37,12 @@ import com.tangem.features.tangempay.components.TangemPayDetailsComponent import com.tangem.features.tokendetails.TokenDetailsComponent import com.tangem.features.wallet.WalletEntryComponent import com.tangem.features.walletconnect.components.WalletConnectEntryComponent -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent import com.tangem.tap.features.details.ui.cardsettings.coderecovery.api.AccessCodeRecoveryComponent import com.tangem.tap.features.details.ui.resetcard.api.ResetCardComponent import com.tangem.tap.features.details.ui.securitymode.api.SecurityModeComponent -import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent import com.tangem.tap.features.welcome.component.WelcomeComponent import com.tangem.tap.routing.component.RoutingComponent.Child import com.tangem.features.tangempay.components.TangemPayOnboardingComponent @@ -84,7 +74,6 @@ internal class ChildFactory @Inject constructor( private val swapComponentFactory: SwapComponent.Factory, private val homeComponentFactory: HomeComponent.Factory, private val tokenDetailsComponentFactory: TokenDetailsComponent.Factory, - private val walletConnectComponentFactory: WalletConnectComponent.Factory, private val qrScanningComponentFactory: QrScanningComponent.Factory, private val accessCodeRecoveryComponentFactory: AccessCodeRecoveryComponent.Factory, private val cardSettingsComponentFactory: CardSettingsComponent.Factory, @@ -116,7 +105,6 @@ internal class ChildFactory @Inject constructor( private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory, private val tangemPayDetailsComponentFactory: TangemPayDetailsComponent.Factory, private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory, - private val walletConnectFeatureToggles: WalletConnectFeatureToggles, private val hotWalletFeatureToggles: HotWalletFeatureToggles, ) { @@ -329,19 +317,11 @@ internal class ChildFactory @Inject constructor( ) } is AppRoute.WalletConnectSessions -> { - if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) { - createComponentChild( - context = context, - params = RedesignedWalletConnectComponent.Params(route.userWalletId), - componentFactory = redesignedWalletConnectComponentFactory, - ) - } else { - createComponentChild( - context = context, - params = WalletConnectComponent.Params(route.userWalletId), - componentFactory = walletConnectComponentFactory, - ) - } + createComponentChild( + context = context, + params = RedesignedWalletConnectComponent.Params(route.userWalletId), + componentFactory = redesignedWalletConnectComponentFactory, + ) } is AppRoute.QrScanning -> { val source = when (route.source) { diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index ec0d4d8507..b22c123693 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -19,10 +19,6 @@ "name": "STAKING_CARDANO_ENABLED", "version": "undefined" }, - { - "name": "WALLET_CONNECT_REDESIGN_ENABLED", - "version": "5.27.0" - }, { "name": "USEDESK_ENABLED", "version": "undefined" diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt index 0fe3412528..a83cefd1ae 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt @@ -24,7 +24,6 @@ import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.walletconnect.WcPairService import com.tangem.domain.walletconnect.WcRequestService import com.tangem.domain.walletconnect.WcRequestUseCaseFactory -import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository import com.tangem.domain.walletconnect.repository.WalletConnectRepository import com.tangem.domain.walletconnect.repository.WcSessionsManager import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase @@ -88,7 +87,6 @@ internal object WalletConnectDataModule { fun defaultWcSessionsManager( store: WalletConnectStore, dispatchers: CoroutineDispatcherProvider, - legacyStore: WalletConnectSessionsRepository, getWallets: GetWalletsUseCase, wcNetworksConverter: WcNetworksConverter, analytics: AnalyticsEventHandler, @@ -97,7 +95,6 @@ internal object WalletConnectDataModule { return DefaultWcSessionsManager( store = store, dispatchers = dispatchers, - legacyStore = legacyStore, getWallets = getWallets, wcNetworksConverter = wcNetworksConverter, analytics = analytics, diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt index 69aa56551c..8697778af1 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt @@ -3,7 +3,6 @@ package com.tangem.data.walletconnect.sessions import arrow.core.Either import arrow.core.left 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 @@ -16,7 +15,6 @@ import com.tangem.domain.models.wallet.UserWallet 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 import com.tangem.domain.walletconnect.repository.WcSessionsManager import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -30,7 +28,6 @@ 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 wcNetworksConverter: WcNetworksConverter, @@ -39,18 +36,12 @@ internal class DefaultWcSessionsManager( ) : WcSessionsManager, WcSdkObserver { private val onSessionDelete = Channel(capacity = Channel.BUFFERED) - private val oneTimeMigration = MutableStateFlow(true) override val sessions: Flow>> get() = combine(getWallets(), store.sessions) { wallets, inStore -> wallets to inStore } .transform { pair -> val (wallets, inStore) = pair val inSdk: List = WalletKit.getListOfActiveSessions() - if (oneTimeMigration.value) { - oneTimeMigration.value = false - val someMigrated = migrateLegacyStore(inStore, inSdk, wallets) - if (someMigrated) return@transform // ignore emit, wait next one - } val associatedSessions: List = associate(inSdk, inStore, wallets) val someRemove = removeUnknownSessions(inStore, inSdk, associatedSessions) if (someRemove) return@transform // ignore emit, wait next one @@ -60,7 +51,6 @@ internal class DefaultWcSessionsManager( .flowOn(dispatchers.io) override fun onWcSdkInit() { - oneTimeMigration.value = true listenOnSessionDelete() extendSessions() } @@ -96,39 +86,6 @@ internal class DefaultWcSessionsManager( onSessionDelete.trySend(sessionDelete) } - private suspend fun migrateLegacyStore( - inNewStore: Set, - inSdk: List, - wallets: List, - ): Boolean { - val walletIds = wallets.map { wallet -> wallet.walletId } - val inLegacyStore = walletIds - .map { walletId -> - flow { - emit( - legacyStore.loadSessions(walletId.stringValue).mapNotNull { legacySession -> - val url = - inSdk.find { it.topic == legacySession.topic }?.metaData?.url ?: return@mapNotNull null - WcSessionDTO( - topic = legacySession.topic, - walletId = walletId, - url = url, - securityStatus = CheckDAppResult.FAILED_TO_VERIFY, - ) - }, - ) - } - } - .merge() - .reduce { accumulator, value -> accumulator.plus(value) } - // migrate only active legacySessions - .filter { legacySession -> inSdk.any { inSdkSession -> inSdkSession.topic == legacySession.topic } } - - val mustSaveInNewStore = inLegacyStore.subtract(inNewStore) - if (mustSaveInNewStore.isNotEmpty()) store.saveSessions(mustSaveInNewStore) - return mustSaveInNewStore.isNotEmpty() - } - private suspend fun associate( inSdk: List, inStore: Set, diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Account.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Account.kt deleted file mode 100644 index 8c370b9fbd..0000000000 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Account.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.domain.walletconnect.model.legacy - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class Account( - @Json(name = "chainId") - val chainId: String, - - @Json(name = "walletAddress") - val walletAddress: String, - - @Json(name = "derivationPath") - val derivationPath: String?, -) \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Session.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Session.kt deleted file mode 100644 index 5ca9f80ed4..0000000000 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/Session.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.domain.walletconnect.model.legacy - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class Session( - @Json(name = "topic") - val topic: String, - - @Json(name = "accounts") - val accounts: List, -) \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/WalletConnectSessionsRepository.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/WalletConnectSessionsRepository.kt deleted file mode 100644 index dce7f7f080..0000000000 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/legacy/WalletConnectSessionsRepository.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.domain.walletconnect.model.legacy - -interface WalletConnectSessionsRepository { - suspend fun loadSessions(userWallet: String): List - - suspend fun saveSession(userWallet: String, session: Session) - - suspend fun removeSession(userWallet: String, topic: String) -} \ No newline at end of file diff --git a/features/walletconnect/api/src/main/kotlin/com/tangem/features/walletconnect/components/WalletConnectFeatureToggles.kt b/features/walletconnect/api/src/main/kotlin/com/tangem/features/walletconnect/components/WalletConnectFeatureToggles.kt deleted file mode 100644 index 3b9e35b56a..0000000000 --- a/features/walletconnect/api/src/main/kotlin/com/tangem/features/walletconnect/components/WalletConnectFeatureToggles.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.tangem.features.walletconnect.components - -interface WalletConnectFeatureToggles { - val isRedesignedWalletConnectEnabled: Boolean -} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/WcRoutingModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/WcRoutingModel.kt index 5524f954e3..341ab307f2 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/WcRoutingModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/WcRoutingModel.kt @@ -10,7 +10,6 @@ import com.tangem.domain.walletconnect.WcRequestService import com.tangem.domain.walletconnect.model.WcEthMethodName import com.tangem.domain.walletconnect.model.WcMethodName import com.tangem.domain.walletconnect.model.WcSolanaMethodName -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import javax.inject.Inject @@ -21,18 +20,15 @@ internal class WcRoutingModel @Inject constructor( private val pairService: WcPairService, private val cardSdkProvider: CardSdkProvider, override val dispatchers: CoroutineDispatcherProvider, - private val featureToggles: WalletConnectFeatureToggles, ) : Model() { - val innerRouter = WcRouter(SlotNavigation()) + val innerRouter = WcRouter(SlotNavigation()) private val isSlotEmpty = MutableStateFlow(true) private val permittedAppRoute = MutableStateFlow(false) init { - if (featureToggles.isRedesignedWalletConnectEnabled) { - setupQueue() - } + setupQueue() } fun onSlotEmpty() { diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/deeplink/DefaultWalletConnectDeepLinkHandler.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/deeplink/DefaultWalletConnectDeepLinkHandler.kt index 53c98210ad..5d82c115da 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/deeplink/DefaultWalletConnectDeepLinkHandler.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/deeplink/DefaultWalletConnectDeepLinkHandler.kt @@ -4,7 +4,6 @@ import android.net.Uri import com.tangem.domain.walletconnect.WcPairService import com.tangem.domain.walletconnect.model.WcPairRequest import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles import com.tangem.features.walletconnect.components.deeplink.WalletConnectDeepLinkHandler import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -15,7 +14,6 @@ import java.net.URLDecoder internal class DefaultWalletConnectDeepLinkHandler @AssistedInject constructor( @Assisted uri: Uri, getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - walletConnectFeatureToggles: WalletConnectFeatureToggles, wcPairService: WcPairService, ) : WalletConnectDeepLinkHandler { @@ -30,15 +28,13 @@ internal class DefaultWalletConnectDeepLinkHandler @AssistedInject constructor( }, ifRight = { wallet -> val decodedWcUri = URLDecoder.decode(wcUri, DEFAULT_CHARSET_NAME) - if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) { - wcPairService.pair( - request = WcPairRequest( - uri = decodedWcUri, - source = WcPairRequest.Source.DEEPLINK, - userWalletId = wallet.walletId, - ), - ) - } + wcPairService.pair( + request = WcPairRequest( + uri = decodedWcUri, + source = WcPairRequest.Source.DEEPLINK, + userWalletId = wallet.walletId, + ), + ) }, ) } catch (e: Exception) { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 37e38fce69..559f845100 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -63,7 +63,6 @@ hilt-work = "1.0.0" hilt-compilerx = "1.0.0" jodatime = "2.12.1" kotlin-immutable-collections = "0.3.5" -kotsonGsonExt = "2.5.0" lottie = "3.4.0" lottie-compose = "6.6.0" moshi = "1.15.1" @@ -252,7 +251,6 @@ hilt-core = { module = "com.google.dagger:hilt-core", version.ref = "hilt" } hilt-kapt = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } hilt-compilerx = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt-compilerx" } jodatime = { module = "joda-time:joda-time", version.ref = "jodatime" } -kotsonGson = { module = "com.github.salomonbrys.kotson:kotson", version.ref = "kotsonGsonExt" } lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" } lottie-compose = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie-compose" } material = { module = "com.google.android.material:material", version.ref = "googleMaterialComponent" }