diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..57188dd623 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +[*.{kt,kts}] +ktlint_code_style = official +ktlint_ignore_back_ticked_identifier = true + +indent_size = 4 +indent_style = space +insert_final_newline = true + +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true + +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.** + +max_line_length = 120 diff --git a/.gitignore b/.gitignore index 4cc00a7e1f..dd64e70b22 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ local.properties # User-specific configurations .idea +!.idea/codeStyles +!.idea/inspectionProfiles # LayoutInspector files /captures @@ -26,4 +28,4 @@ local.properties **/fastlane/report.xml **/fastlane/Preview.html **/fastlane/screenshots -**/fastlane/test_output \ No newline at end of file +**/fastlane/test_output diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..ad9c1261c4 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,204 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..ba5af7a4ce --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,92 @@ + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 3f12b8e345..105d4d8323 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,13 +6,13 @@ apply plugin: 'com.google.firebase.crashlytics' apply from: '../dependencies.gradle' android { - compileSdkVersion 31 + compileSdkVersion 32 defaultConfig { applicationId "com.tangem.wallet" versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 1 versionName project.hasProperty('versionName') ? project.property('versionName') : "1.0.0.-SNAPSHOT" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 32 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' @@ -83,12 +83,11 @@ dependencies { implementation implementation(project(path: ':network')) implementation implementation(project(path: ':common')) - implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.fragment:fragment-ktx:1.4.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'com.google.android.material:material:1.5.0' - implementation "androidx.core:core-ktx:1.7.0" implementation 'com.google.android.play:core:1.10.3' implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' @@ -164,14 +163,14 @@ dependencies { implementation 'com.google.android.gms:play-services-wallet:19.1.0' //Compose - implementation 'androidx.activity:activity-compose:1.4.0' + implementation 'androidx.activity:activity-compose:1.5.0' implementation 'androidx.compose.material:material:1.1.1' implementation 'androidx.compose.animation:animation:1.1.1' - implementation 'androidx.compose.foundation:foundation:1.1.1' - implementation 'androidx.compose.foundation:foundation-layout:1.1.1' + implementation 'androidx.compose.foundation:foundation:1.2.0-rc03' implementation 'androidx.compose.ui:ui:1.1.1' implementation 'androidx.compose.ui:ui-tooling:1.1.1' implementation "com.google.accompanist:accompanist-appcompat-theme:0.23.0" + implementation "com.google.accompanist:accompanist-systemuicontroller:0.23.0" implementation "com.github.skydoves:androidveil:1.1.2" diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 7593f303e7..78d55638ca 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -5,6 +5,8 @@ import android.content.pm.ActivityInfo import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsControllerCompat import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.snackbar.Snackbar import com.tangem.TangemSdk @@ -27,11 +29,11 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.wallet.R import com.tangem.wallet.databinding.ActivityMainBinding -import java.lang.ref.WeakReference -import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import java.lang.ref.WeakReference +import kotlin.coroutines.CoroutineContext lateinit var tangemSdk: TangemSdk lateinit var tangemSdkManager: TangemSdkManager @@ -83,8 +85,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { } private fun systemActions() { - // makes the status bar text dark - window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + WindowCompat.setDecorFitsSystemWindows(window, false) + WindowInsetsControllerCompat(window, binding.root) + .isAppearanceLightStatusBars = true + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } 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 a199be957b..e601bdc95c 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -107,6 +107,12 @@ class DialogManager : StoreSubscriber { dAppName = state.dialog.dAppName, context = context ) + is WalletConnectDialog.UnsupportedNetwork -> + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_scanner_error_unsupported_network, + context = context + ) is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context) is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context) is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 26ec96a604..7589f86c3a 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -63,7 +63,11 @@ fun FragmentActivity.popBackTo(screen: AppScreen?, inclusive: Boolean = false) { } fun FragmentActivity.getPreviousScreen(): AppScreen? { - val indexOfLastFragment = this.supportFragmentManager.backStackEntryCount - 1 + val indexOfLastFragment = if (this.supportFragmentManager.backStackEntryCount > 0) { + this.supportFragmentManager.backStackEntryCount - 1 + } else { + 0 + } val tag = if (indexOfLastFragment < this.supportFragmentManager.backStackEntryCount) this.supportFragmentManager.getBackStackEntryAt(indexOfLastFragment).name else null diff --git a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt b/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt index 3f8fa992ff..961e77e8f7 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt @@ -19,4 +19,6 @@ fun StringBuilder.appendIfNotNull(value: String?, prefix: String? = null, postfi fun String.appendIfNotNull(value: String?, prefix: String? = null, postfix: String? = null): String { return StringBuilder(this).apply { appendIfNotNull(value, prefix, postfix) }.toString() -} \ No newline at end of file +} + +fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count)) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 40acdd1b7d..39e0103066 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -8,7 +8,7 @@ import com.tangem.tap.common.TestActions import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.amountToCreateAccount import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.features.demo.isDemoWallet +import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.reducers.createAddressesData import com.tangem.tap.network.NetworkConnectivity @@ -21,7 +21,10 @@ import timber.log.Timber [REDACTED_AUTHOR] */ suspend fun WalletManager.safeUpdate(): Result = try { - if (isDemoWallet() || TestActions.testAmountInjectionForWalletManagerEnabled) { + val scanResponse = store.state.globalState.scanResponse + ?: error("Scan response must not be null") + + if (scanResponse.isDemoCard() || TestActions.testAmountInjectionForWalletManagerEnabled) { delay(500) TestActions.testAmountInjectionForWalletManagerEnabled = false Result.Success(wallet) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt index dc2a7d07d5..6578b97356 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt @@ -2,6 +2,7 @@ package com.tangem.tap.common.feedback import android.content.Context import com.tangem.domain.common.TapWorkarounds +import com.tangem.tap.common.extensions.breakLine import com.tangem.wallet.R interface FeedbackData { @@ -17,9 +18,9 @@ interface FeedbackData { fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String { return StringBuilder().apply { append(context.getString(mainMessageResId)) - appendLine(3) + breakLine(3) append(context.getString(getDataCollectionMessageResId())) - appendLine() + breakLine() append(createOptionalMessage(infoHolder)) }.toString() } @@ -31,7 +32,7 @@ class RateCanBeBetterEmail : FeedbackData { override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) .appendCardInfo() - .appendLine() + .breakLine() .appendPhoneInfo() .build() } @@ -43,7 +44,7 @@ class ScanFailsEmail : FeedbackData { override fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String = StringBuilder().apply { append(context.getString(mainMessageResId)) - appendLine(4) + breakLine(4) append(createOptionalMessage(infoHolder)) }.toString() @@ -53,7 +54,7 @@ class ScanFailsEmail : FeedbackData { } class SendTransactionFailedEmail( - val error: String + val error: String, ) : FeedbackData { override val subjectResId: Int = R.string.feedback_subject_tx_failed @@ -63,7 +64,7 @@ class SendTransactionFailedEmail( .appendCardInfo() .appendDelimiter() .appendTxFailedBlockchainInfo(error) - .appendLine() + .breakLine() .appendPhoneInfo() .build() } @@ -89,7 +90,7 @@ class FeedbackEmail : FeedbackData { override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) .appendCardInfo() .appendWalletsInfo() - .appendLine() + .breakLine() .appendPhoneInfo() .build() } diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt index 9e5378e9e5..a3a9ee5575 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt @@ -1,5 +1,7 @@ package com.tangem.tap.common.feedback +import com.tangem.tap.common.extensions.breakLine + class FeedbackDataBuilder( private val infoHolder: AdditionalFeedbackInfo ) { @@ -10,8 +12,8 @@ class FeedbackDataBuilder( return this } - fun appendLine(count: Int = 1): FeedbackDataBuilder { - builder.appendLine(count) + fun breakLine(count: Int = 1): FeedbackDataBuilder { + builder.breakLine(count) return this } @@ -34,7 +36,7 @@ class FeedbackDataBuilder( infoHolder.tokens[it.blockchain]?.let { tokens -> builder.append("Tokens:") - appendLine() + breakLine() tokens.forEach { token -> builder.appendKeyValue("Name", token.name) builder.appendKeyValue("ID", token.id ?: "[custom token]") @@ -74,8 +76,4 @@ private fun StringBuilder.appendKeyValue(key: String, value: String): StringBuil return if (value.isNotBlank()) this.append("$key: $value\n") else this } -private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n") - -private fun StringBuilder.appendLine(count: Int = 1): StringBuilder { - return append(List(count) { "\n" }.joinToString(separator = "")) -} \ No newline at end of file +private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n") \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index 20d1eda08c..9e22833682 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -5,44 +5,25 @@ import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.Wallet import com.tangem.common.CompletionResult -import com.tangem.tap.domain.tasks.SignHashTask +import com.tangem.common.card.Card import com.tangem.tap.domain.tasks.SignHashesTask import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine class TangemSigner( + private val card: Card, private val tangemSdk: TangemSdk, private val initialMessage: Message, private val signerCallback: (TangemSignerResponse) -> Unit, ) : TransactionSigner { - override suspend fun sign(hash: ByteArray, cardId: String, publicKey: Wallet.PublicKey): CompletionResult { + override suspend fun sign( + hashes: List, + publicKey: Wallet.PublicKey + ): CompletionResult> { return suspendCoroutine { continuation -> - val command = SignHashTask(hash, publicKey) - tangemSdk.startSessionWithRunnable( - runnable = command, - cardId = cardId, - initialMessage = initialMessage, - ) { result -> - when (result) { - is CompletionResult.Success -> { - signerCallback( - TangemSignerResponse( - result.data.totalSignedHashes, - result.data.remainingSignatures - ) - ) - continuation.resume(CompletionResult.Success(result.data.signature)) - } - is CompletionResult.Failure -> - continuation.resume(CompletionResult.Failure(result.error)) - } - } - } - } + val cardId = if (card.backupStatus?.isActive == true) null else card.cardId - override suspend fun sign(hashes: List, cardId: String, publicKey: Wallet.PublicKey): CompletionResult> { - return suspendCoroutine { continuation -> val task = SignHashesTask(hashes, publicKey) tangemSdk.startSessionWithRunnable( runnable = task, @@ -65,6 +46,21 @@ class TangemSigner( } } } + + override suspend fun sign( + hash: ByteArray, + publicKey: Wallet.PublicKey + ): CompletionResult { + val result = sign( + hashes = listOf(hash), + publicKey = publicKey + ) + + return when (result) { + is CompletionResult.Success -> CompletionResult.Success(result.data.first()) + is CompletionResult.Failure -> CompletionResult.Failure(result.error) + } + } } data class TangemSignerResponse( diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index e636c4fa07..34b8289884 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -32,9 +32,10 @@ fun WalletManagerFactory.makeWalletManagerForApp( return when { scanResponse.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> { makeTwinWalletManager( - card.cardId, - wallet.publicKey, scanResponse.secondTwinPublicKey!!.hexToBytes(), - environmentBlockchain, wallet.curve + walletPublicKey = wallet.publicKey, + pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(), + blockchain = environmentBlockchain, + curve = wallet.curve ) } seedKey != null && derivationParams != null -> { @@ -47,7 +48,6 @@ fun WalletManagerFactory.makeWalletManagerForApp( ?: return null makeWalletManager( - cardId = card.cardId, blockchain = environmentBlockchain, seedKey = wallet.publicKey, derivedKey = derivedKey, @@ -56,7 +56,6 @@ fun WalletManagerFactory.makeWalletManagerForApp( } else -> { makeWalletManager( - cardId = card.cardId, blockchain = environmentBlockchain, walletPublicKey = wallet.publicKey, curve = wallet.curve diff --git a/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt b/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt index 07ac4886bb..57762aaf51 100644 --- a/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt +++ b/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt @@ -57,7 +57,6 @@ class WalletStateConverter : StringStateConverter { walletMap["publicKey"] = publicKeyMap walletMap["amounts"] = amounts walletMap["addresses"] = wallet.addresses.toString() - walletMap["cardId"] = wallet.cardId return walletMap } diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt index 032c0b97ca..f05065bc8d 100644 --- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -4,16 +4,17 @@ import android.content.res.Resources import android.net.Uri import androidx.core.os.ConfigurationCompat import com.tangem.common.card.Card -import java.util.* +import java.util.Locale /** [REDACTED_AUTHOR] */ class CardTou { - private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) + private val locale: Locale = + ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0)!! fun getUrl(card: Card): Uri? { - val issuerName = card.issuer.name ?: return null + val issuerName = card.issuer.name if (issuerName.lowercase(Locale.getDefault()) != "start2coin") return null val baseUrl = "https://app.tangem.com/tou/" 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 index fe8a593787..b366229775 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt @@ -1,11 +1,13 @@ package com.tangem.tap.domain.walletconnect import com.github.salomonbrys.kotson.fromJson +import com.github.salomonbrys.kotson.toMap import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.JsonArray import com.google.gson.JsonParser import com.google.gson.annotations.SerializedName +import com.tangem.blockchain.common.Blockchain import com.trustwallet.walletconnect.JSONRPC_VERSION import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage @@ -18,9 +20,11 @@ class EthSignHelper { .create() } - fun tryToParseEthTypedMessage(data: String): WCEthereumSignMessage? { - val request = - gson.fromJson(data) + fun parseCustomRequest(data: String): CustomJsonRpcRequest { + return gson.fromJson(data) + } + + fun tryToParseEthTypedMessage(request: CustomJsonRpcRequest): WCEthereumSignMessage? { return if (request.method == WCMethodExtended.ETH_SIGN_TYPE_DATA_V4) { WCEthereumSignMessage( listOf( @@ -53,12 +57,26 @@ class EthSignHelper { } } -data class CustomJsonRpcRequests( +data class CustomJsonRpcRequest( val id: Long, val jsonrpc: String = JSONRPC_VERSION, val method: WCMethodExtended?, val params: JsonArray -) +) { + + fun blockchainFromChainId(): Blockchain? { + return try { + val hex = params[0].asJsonObject.toMap()[CHAIN_ID_KEY]?.asString ?: "" + Blockchain.fromChainId(Integer.decode(hex)) + } catch (exception: Exception) { + null + } + } + + companion object { + const val CHAIN_ID_KEY = "chainId" + } +} enum class WCMethodExtended { @SerializedName("wc_sessionRequest") @@ -95,5 +113,10 @@ enum class WCMethodExtended { GET_ACCOUNTS, @SerializedName("trust_signTransaction") - SIGN_TRANSACTION; + SIGN_TRANSACTION, + + @SerializedName("wallet_switchEthereumChain") + SWITCH_CHAIN, + + ; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index dd94266b06..7d36064f1e 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -11,14 +11,20 @@ import com.tangem.tap.store import com.tangem.tap.walletConnectRepository import com.trustwallet.walletconnect.WCClient import com.trustwallet.walletconnect.models.WCPeerMeta -import com.trustwallet.walletconnect.models.binance.* +import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder +import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction import com.trustwallet.walletconnect.models.session.WCSession import com.trustwallet.walletconnect.models.session.WCSessionUpdate import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import okhttp3.* +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import java.util.* @@ -71,6 +77,22 @@ class WalletConnectManager { } } + fun updateBlockchain(session: WalletConnectSession) { + sessions[session.session]?.client?.updateSession( + accounts = listOfNotNull(session.getAddress()), + chainId = session.wallet.blockchain?.getChainId(), + approved = true + ) + + + val updatedSession = sessions[session.session]?.copy( + wallet = session.wallet + ) + if (updatedSession != null) { + sessions[session.session] = updatedSession + } + } + private fun setupConnectionTimeoutCheck(session: WCSession) { scope.launch { delay(20_000) @@ -409,23 +431,43 @@ class WalletConnectManager { Timber.d("Custom Request") Timber.d(data) - val message = EthSignHelper.tryToParseEthTypedMessage(data) - if (message != null) { - Timber.d("onEthSign_v4: $message") - store.state.globalState.analyticsHandlers?.logWcEvent( - Analytics.WcAnalyticsEvent.Action( - Analytics.WcAction.PersonalSign + val request = EthSignHelper.parseCustomRequest(data) + + when (request.method) { + WCMethodExtended.ETH_SIGN_TYPE_DATA_V4 -> handleTypedDataV4(request, client, id) + WCMethodExtended.SWITCH_CHAIN -> { + val blockchain = request.blockchainFromChainId() + Timber.d("WC switch chainID\nNew Blockchain: $blockchain") + val session = sessions[client.session]?.toWalletConnectSession() + if (session != null) { + store.dispatchOnMain(WalletConnectAction.SwitchBlockchain(blockchain, session)) + } + } + else -> { + Timber.d("WC: unrecognized custom request") + } + } + } + + } + + private fun handleTypedDataV4(request: CustomJsonRpcRequest, client: WCClient, id: Long) { + val message = EthSignHelper.tryToParseEthTypedMessage(request) + if (message != null) { + Timber.d("onEthSign_v4: $message") + store.state.globalState.analyticsHandlers?.logWcEvent( + Analytics.WcAnalyticsEvent.Action( + Analytics.WcAction.PersonalSign + ) + ) + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain( + WalletConnectAction.HandlePersonalSignRequest( + message, + sessionData, + id ) ) - sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> - store.dispatchOnMain( - WalletConnectAction.HandlePersonalSignRequest( - message, - sessionData, - id - ) - ) - } } } } 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 index 1d0dc4a9e0..a3b0d31a89 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -5,8 +5,17 @@ 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.blockchains.ethereum.EthereumUtils.Companion.toKeccak -import com.tangem.blockchain.common.* -import com.tangem.blockchain.extensions.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.CommonSigner +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.extensions.hexToBigDecimal +import com.tangem.blockchain.extensions.isAscii import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes @@ -17,7 +26,11 @@ import com.tangem.operations.sign.SignHashCommand import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.extensions.toFormattedString -import com.tangem.tap.features.details.redux.walletconnect.* +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession +import com.tangem.tap.features.details.redux.walletconnect.WalletForSession +import com.tangem.tap.features.details.redux.walletconnect.WcPersonalSignData +import com.tangem.tap.features.details.redux.walletconnect.WcTransactionData +import com.tangem.tap.features.details.redux.walletconnect.WcTransactionType import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData import com.tangem.tap.store @@ -25,8 +38,8 @@ import com.tangem.tap.tangemSdk import com.tangem.tap.tangemSdkManager import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction -import timber.log.Timber import java.math.BigDecimal +import timber.log.Timber class WalletConnectSdkHelper { @@ -108,9 +121,8 @@ class WalletConnectSdkHelper { ) val blockchain = session.wallet.getBlockchainForSession() return factory.makeWalletManager( - session.wallet.cardId, - blockchain, - publicKey + blockchain = blockchain, + publicKey = publicKey ) } @@ -124,7 +136,7 @@ class WalletConnectSdkHelper { private suspend fun sendTransaction(data: WcTransactionData): String? { val result = (data.walletManager as TransactionSender).send( transactionData = data.transaction, - signer = Signer(tangemSdk) + signer = CommonSigner(tangemSdk) ) return when (result) { SimpleResult.Success -> { diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt new file mode 100644 index 0000000000..7c978b7b6f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WcWalletManagerFactory.kt @@ -0,0 +1,95 @@ +package com.tangem.tap.domain.walletconnect + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.common.card.EllipticCurve +import com.tangem.domain.common.ScanResponse +import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.tap.domain.extensions.getPrimaryCurve +import com.tangem.tap.domain.extensions.makeWalletManagerForApp +import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.tokens.models.BlockchainNetwork +import com.tangem.tap.features.details.redux.walletconnect.WalletForSession +import com.tangem.tap.features.wallet.redux.WalletState + +class WcWalletManagerFactory( + private val factory: WalletManagerFactory, + private val currenciesRepository: CurrenciesRepository, + ) { + + suspend fun getWalletManager( + wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState + ): WalletManager? { + val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) { + Blockchain.EthereumTestnet + } else { + blockchain + } + + val blockchainNetwork = BlockchainNetwork( + blockchain = blockchainToMake, + derivationPath = wallet.derivationPath?.rawPath, + tokens = emptyList() + ) + + return if (walletState.cardId == wallet.cardId) { + walletState.getWalletManager(blockchainNetwork) + } else { + val blockchainNetworkWithTokens = currenciesRepository + .loadSavedCurrencies( + cardId = wallet.cardId, + isHdWalletSupported = wallet.derivationPath != null + ).firstOrNull { it == blockchainNetwork } + + if (blockchainNetworkWithTokens != null) { + factory.makeWalletManager( + cardId = wallet.cardId, + blockchain = blockchainToMake, + publicKey = Wallet.PublicKey( + wallet.walletPublicKey!!, + wallet.derivedPublicKey, + wallet.derivationPath + ), + tokens = blockchainNetworkWithTokens.tokens, + curve = blockchainToMake.getPrimaryCurve() ?: EllipticCurve.Secp256k1 + ) + } else { + null + } + } + } + + suspend fun getWalletManager( + scanResponse: ScanResponse, blockchain: Blockchain, walletState: WalletState + ): WalletManager? { + val card = scanResponse.card + val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) { + Blockchain.EthereumTestnet + } else { + blockchain + } + + val blockchainNetwork = BlockchainNetwork( + blockchain = blockchainToMake, + card = card + ) + + return if (walletState.cardId == card.cardId) { + walletState.getWalletManager(blockchainNetwork) + } else { + if (currenciesRepository + .loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) + .contains(blockchainNetwork) + ) { + factory.makeWalletManagerForApp( + scanResponse = scanResponse, + blockchainNetwork = blockchainNetwork + ) + } else { + null + } + } + } +} \ 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 7fe717e1a5..f7ccf6156a 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 @@ -109,7 +109,7 @@ class DemoConfig { testDemoCardIds).distinct() } - private val releaseDemoCardIds = mutableListOf( + private val releaseDemoCardIds = mutableListOf( // Tangem Wallet: "AC01000000041100", "AC01000000042462", @@ -284,7 +284,10 @@ class DemoTransactionSender( override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { val dataToSign = randomString(32).toByteArray() - val signerResponse = signer.sign(dataToSign, walletManager.wallet.cardId, walletManager.wallet.publicKey) + val signerResponse = signer.sign( + hash = dataToSign, + publicKey = walletManager.wallet.publicKey + ) return when (signerResponse) { is CompletionResult.Success -> SimpleResult.Failure(Exception(ID)) is CompletionResult.Failure -> SimpleResult.fromTangemSdkError(signerResponse.error) diff --git a/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt b/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt index 99ecfbc227..66d09bdbf0 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt @@ -1,10 +1,10 @@ package com.tangem.tap.features.demo -import com.tangem.blockchain.common.WalletManager +import com.tangem.common.card.Card import com.tangem.domain.common.ScanResponse /** [REDACTED_AUTHOR] */ fun ScanResponse.isDemoCard(): Boolean = DemoHelper.isDemoCardId(card.cardId) -fun WalletManager.isDemoWallet(): Boolean = DemoHelper.isDemoCardId(wallet.cardId) \ No newline at end of file +fun Card.isDemoCard(): Boolean = DemoHelper.isDemoCardId(cardId) \ No newline at end of file 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 index a180d20d4a..471ccaa44c 100644 --- 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 @@ -1,6 +1,7 @@ package com.tangem.tap.features.details.redux.walletconnect import android.app.Activity +import com.tangem.blockchain.common.Blockchain import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.wallet.R @@ -47,6 +48,16 @@ sealed class WalletConnectAction : Action { data class Success(val session: WalletConnectSession) : WalletConnectAction() } + data class SwitchBlockchain( + val blockchain: Blockchain?, + val session: WalletConnectSession + ) : WalletConnectAction() + + data class UpdateBlockchain( + val updatedSession: WalletConnectSession + ) : WalletConnectAction() + + data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction() data class SetSessionsRestored(val sessions: List) : 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 index 25c5d373b4..7f7a88748a 100644 --- 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 @@ -1,10 +1,8 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.guard import com.tangem.domain.common.ScanResponse -import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.getFromClipboard @@ -14,11 +12,10 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.extensions.isMultiwalletAllowed -import com.tangem.tap.domain.extensions.makeWalletManagerForApp -import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.walletconnect.BnbHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils +import com.tangem.tap.domain.walletconnect.WcWalletManagerFactory import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.scope import com.tangem.tap.store @@ -159,6 +156,45 @@ class WalletConnectMiddleware { action.id, action.data, action.sessionData ) } + is WalletConnectAction.SwitchBlockchain -> { + val blockchain = action.blockchain.guard { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork)) + return + } + + val factory = WcWalletManagerFactory( + factory = store.state.globalState.tapWalletManager.walletManagerFactory, + currenciesRepository = currenciesRepository + ) + val walletState = store.state.walletState + scope.launch { + val walletManager = factory.getWalletManager( + wallet = action.session.wallet, + blockchain = blockchain, + walletState = walletState + ).guard { + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.AddNetwork(blockchain.fullName) + ) + ) + return@launch + } + + val updatedWallet = action.session.wallet.copy( + walletPublicKey = walletManager.wallet.publicKey.seedKey, + derivedPublicKey = walletManager.wallet.publicKey.derivedKey, + derivationPath = walletManager.wallet.publicKey.derivationPath, + blockchain = action.blockchain + + ) + val updatedSession = action.session.copy(wallet = updatedWallet) + store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession)) + } + } + is WalletConnectAction.UpdateBlockchain -> { + walletConnectManager.updateBlockchain(action.updatedSession) + } } } @@ -189,8 +225,13 @@ class WalletConnectMiddleware { return } + val factory = WcWalletManagerFactory( + factory = store.state.globalState.tapWalletManager.walletManagerFactory, + currenciesRepository = currenciesRepository + ) + val walletState = store.state.walletState scope.launch { - val walletManager = getWalletManager(scanResponse, blockchain).guard { + val walletManager = factory.getWalletManager(scanResponse, blockchain, walletState).guard { store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session)) store.dispatchOnMain( GlobalAction.ShowDialog( @@ -232,37 +273,4 @@ class WalletConnectMiddleware { } } - - private suspend fun getWalletManager( - scanResponse: ScanResponse, blockchain: Blockchain - ): WalletManager? { - val card = scanResponse.card - val factory = store.state.globalState.tapWalletManager.walletManagerFactory - val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) { - Blockchain.EthereumTestnet - } else { - blockchain - } - - val blockchainNetwork = BlockchainNetwork( - blockchain = blockchainToMake, - card = card - ) - - return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) { - store.state.walletState.getWalletManager(blockchainNetwork) - } else { - if (currenciesRepository - .loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) - .contains(blockchainNetwork) - ) { - factory.makeWalletManagerForApp( - scanResponse = scanResponse, - blockchainNetwork = blockchainNetwork - ) - } else { - null - } - } - } } \ 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 index 216367e9ee..191fa7b170 100644 --- 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 @@ -34,6 +34,11 @@ class WalletConnectReducer { is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false) is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false) is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false) + is WalletConnectAction.UpdateBlockchain -> state.copy( + sessions = state.sessions + .filterNot { it.peerId == action.updatedSession.peerId } + + action.updatedSession + ) else -> state } 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 index 4f5861c67b..fbd95c6393 100644 --- 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 @@ -82,6 +82,7 @@ data class WalletForSession( sealed class WalletConnectDialog : StateDialog { data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog() object UnsupportedCard : WalletConnectDialog() + object UnsupportedNetwork : WalletConnectDialog() data class AddNetwork(val network: String) : WalletConnectDialog() object OpeningSessionRejected : WalletConnectDialog() object SessionTimeout : WalletConnectDialog() diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index c453cdadc4..f1b3fa3f55 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,10 +1,14 @@ package com.tangem.tap.features.home import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.ComposeView +import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment import com.google.accompanist.appcompattheme.AppCompatTheme import com.tangem.tap.common.redux.navigation.AppScreen @@ -15,48 +19,36 @@ import com.tangem.tap.features.home.redux.HomeState import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.store -import com.tangem.wallet.R import org.rekotlin.StoreSubscriber -class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { +class HomeFragment : Fragment(), StoreSubscriber { - var homeState: MutableState = mutableStateOf(store.state.homeState) + private var homeState: MutableState = mutableStateOf(store.state.homeState) + private var composeView: ComposeView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) store.dispatch(HomeAction.Init) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + val context = container?.context ?: return null store.dispatch(BackupAction.CheckForUnfinishedBackup) - - - getView()?.findViewById(R.id.cv_stories)?.setContent { - AppCompatTheme { - StoriesScreen( - homeState, - onScanButtonClick = { store.dispatch(HomeAction.ReadCard) }, - onShopButtonClick = { - store.dispatch(HomeAction.GoToShop(store.state.globalState.userCountryCode)) - }, - onSearchTokensClick = { - store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens)) - store.dispatch(TokensAction.AllowToAddTokens(false)) - store.dispatch(TokensAction.LoadCurrencies()) - } - ) + composeView = ComposeView(context).apply { + setContent { + AppCompatTheme { + ScreenContent() + } } } - } - private fun getRegionProvider(): RegionProvider = RegionService( - listOf( -// TelephonyManagerRegionProvider(requireContext()), - LocaleRegionProvider() - ) - ) + return composeView + } override fun onStart() { super.onStart() @@ -72,6 +64,11 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber + val signer = TangemSigner( + card = card, + tangemSdk = tangemSdk, + initialMessage = action.messageForSigner + ) { signResponse -> store.dispatch( GlobalAction.UpdateWalletSignedHashes( walletSignedHashes = signResponse.totalSignedHashes, @@ -171,7 +189,7 @@ private fun sendTransaction( ) } val sendResult = try { - if (walletManager.isDemoWallet()) { + if (card.isDemoCard()) { DemoTransactionSender(walletManager).send(txData, signer) } else { (walletManager as TransactionSender).send(txData, signer) @@ -340,5 +358,4 @@ private fun updateWarnings(dispatch: (Action) -> Unit) { val warnings = warningsManager.getWarnings(WarningMessage.Location.SendScreen, listOf(blockchain)) dispatch(SendAction.Warnings.Set(warnings)) -} - +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index ba70c84e05..21489b82f3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -73,6 +73,9 @@ data class WalletState( val walletManagers: List get() = wallets.mapNotNull { it.walletManager } + val cardId: String? + get() = wallets.firstOrNull()?.walletManager?.wallet?.cardId + fun getWalletManager(currency: Currency?): WalletManager? { if (currency?.blockchain == null) return null return getWalletStore(currency)?.walletManager 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 a7ee50446d..064647d863 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 @@ -45,6 +45,7 @@ class TradeCryptoMiddleware { val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return val exchangeManager = store.state.globalState.exchangeManager ?: return + val card = store.state.globalState.scanResponse?.card ?: return val appCurrency = store.state.globalState.appCurrency val addresses = selectedWalletData.walletAddresses?.list.orEmpty() @@ -59,7 +60,13 @@ class TradeCryptoMiddleware { return } - scope.launch { exchangeManager.buyErc20TestnetTokens(walletManager, currency.token) } + scope.launch { + exchangeManager.buyErc20TestnetTokens( + card = card, + walletManager = walletManager, + token = currency.token + ) + } return } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 7a4ac13ea5..7c63b551f9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -1,8 +1,5 @@ package com.tangem.tap.features.wallet.redux.middlewares -import android.content.Intent -import android.net.Uri -import androidx.core.content.ContextCompat import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.AmountType @@ -15,12 +12,7 @@ import com.tangem.domain.common.extensions.withMainContext import com.tangem.operations.attestation.Attestation import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.extensions.copyToClipboard -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.onCardScanned -import com.tangem.tap.common.extensions.shareText -import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen @@ -233,9 +225,7 @@ class WalletMiddleware { action.context.shareText(action.address) } is WalletAction.ExploreAddress -> { - val uri = Uri.parse(action.exploreUrl) - val intent = Intent(Intent.ACTION_VIEW, uri) - ContextCompat.startActivity(action.context, intent, null) + store.dispatchOpenUrl(action.exploreUrl) } is WalletAction.Send -> { val newAction = prepareSendAction(action.amount, store.state.walletState) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 176fa48a14..73f315e29b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import coil.load +import coil.size.Scale import com.tangem.tap.MainActivity import com.tangem.tap.common.extensions.show import com.tangem.tap.common.recyclerView.SpaceItemDecoration @@ -166,6 +167,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber onSuccess() }, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt new file mode 100644 index 0000000000..35d89aaa27 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.wallet.ui.images + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.constraintlayout.utils.widget.ImageFilterView +import com.google.android.material.card.MaterialCardView +import com.tangem.tangem_sdk_new.extensions.dpToPx +import com.tangem.wallet.databinding.ViewCurrencyIconBinding + +class CurrencyIconView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : MaterialCardView(context, attrs, defStyleAttr) { + private val binding = ViewCurrencyIconBinding.inflate( + LayoutInflater.from(context), + this + ) + + val imageView: ImageFilterView + get() = binding.iv + + init { + elevation = 0f + cardElevation = 0f + radius = dpToPx(8f) + background = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index 811c9c0545..8d1085d316 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -7,6 +7,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.extensions.Result +import com.tangem.common.card.Card import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.GlobalAction @@ -68,7 +69,11 @@ class CurrencyExchangeManager( enum class Action { Buy, Sell } } -suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(walletManager: EthereumWalletManager, token: Token) { +suspend fun CurrencyExchangeManager.buyErc20TestnetTokens( + card: Card, + walletManager: EthereumWalletManager, + token: Token, +) { walletManager.safeUpdate() val amountToSend = Amount(walletManager.wallet.blockchain) @@ -85,7 +90,9 @@ suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(walletManager: Ethereu val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress) val signer = TangemSigner( - tangemSdk = tangemSdk, Message() + card = card, + tangemSdk = tangemSdk, + initialMessage = Message(), ) { signResponse -> store.dispatch( GlobalAction.UpdateWalletSignedHashes( diff --git a/app/src/main/res/drawable/ic_overlay.xml b/app/src/main/res/drawable/ic_overlay.xml index 586397f855..5487da88f8 100644 --- a/app/src/main/res/drawable/ic_overlay.xml +++ b/app/src/main/res/drawable/ic_overlay.xml @@ -4,10 +4,18 @@ - - - + + + diff --git a/app/src/main/res/drawable/shape_badge_custom_currency.xml b/app/src/main/res/drawable/shape_badge_custom_currency.xml index 2c3b7f7bd4..cfd740103d 100644 --- a/app/src/main/res/drawable/shape_badge_custom_currency.xml +++ b/app/src/main/res/drawable/shape_badge_custom_currency.xml @@ -7,7 +7,7 @@ android:height="12dp" /> diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_8.xml b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml index a788f39093..4f20cb0e64 100644 --- a/app/src/main/res/drawable/shape_rectangle_rounded_8.xml +++ b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml @@ -5,4 +5,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml deleted file mode 100644 index 7a95116cd8..0000000000 --- a/app/src/main/res/layout/fragment_home.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index e77908ee26..102ae199f4 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -18,14 +18,14 @@ app:liftOnScroll="true"> + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + app:menu="@menu/wallet" + app:navigationIcon="@drawable/ic_wallet_24" + app:navigationIconTint="@color/darkGray6" + app:title="@string/wallet_title" + app:titleCentered="true" /> @@ -43,44 +43,44 @@ android:overScrollMode="never"> + android:id="@+id/cl_wallet" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false" + android:paddingBottom="74dp"> + android:id="@+id/iv_card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="12dp" + android:adjustViewBounds="true" + android:contentDescription="@null" + android:elevation="3dp" + android:maxHeight="215dp" + android:scaleType="fitCenter" + app:layout_constraintTop_toTopOf="parent" + tools:src="@drawable/card_placeholder_black" /> - - - - - - + app:layout_constraintTop_toBottomOf="@id/barrier" /> + + + + + + - + android:id="@+id/rv_multiwallet" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:nestedScrollingEnabled="false" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" /> + android:id="@+id/l_buttons_long" + layout="@layout/layout_wallet_long_buttons" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="30dp" + android:layout_marginEnd="16dp" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/l_address" + app:layout_constraintVertical_bias="1" /> + android:id="@+id/l_buttons_short" + layout="@layout/layout_wallet_short_buttons" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="30dp" + android:layout_marginEnd="16dp" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/l_address" + app:layout_constraintVertical_bias="1" /> + + - - - - - diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml index de692cfa83..9ac49b1ad9 100644 --- a/app/src/main/res/layout/item_currency_wallet.xml +++ b/app/src/main/res/layout/item_currency_wallet.xml @@ -16,45 +16,62 @@ android:elevation="3dp"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:animateLayoutChanges="true" + android:clipToPadding="false"> - + + + + android:id="@+id/badge_custom_balance" + android:layout_width="10dp" + android:layout_height="10dp" + android:background="@drawable/shape_badge_custom_currency" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="@id/iv_currency" + app:layout_constraintEnd_toEndOf="@id/iv_currency" + tools:visibility="visible" /> - + app:layout_constraintTop_toTopOf="parent" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/view_currency_icon.xml b/app/src/main/res/layout/view_currency_icon.xml new file mode 100644 index 0000000000..6859d75600 --- /dev/null +++ b/app/src/main/res/layout/view_currency_icon.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/app/src/main/res/values-de/strings_final.xml b/app/src/main/res/values-de/strings_final.xml index 97ba0b8f36..b16f96f533 100644 --- a/app/src/main/res/values-de/strings_final.xml +++ b/app/src/main/res/values-de/strings_final.xml @@ -59,6 +59,7 @@ The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. %s network not found. Please, add it first and try again. + This network is not supported. Please select another network. %s network Security & Privacy diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml index 5f6407ce70..027a27e7a8 100644 --- a/app/src/main/res/values-fr/strings_final.xml +++ b/app/src/main/res/values-fr/strings_final.xml @@ -59,6 +59,7 @@ The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. %s network not found. Please, add it first and try again. + This network is not supported. Please select another network. %s network Security & Privacy diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml index 5f6407ce70..2645b859e0 100644 --- a/app/src/main/res/values-it/strings_final.xml +++ b/app/src/main/res/values-it/strings_final.xml @@ -59,6 +59,8 @@ The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. %s network not found. Please, add it first and try again. + This network is not supported. Please select another network. + %s network Security & Privacy diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index a7309bed9c..158fab15b2 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -61,9 +61,12 @@ Скрыть Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно. Невозможно скрыть %s - Токен %s является основной валютой в сети %s и не может быть скрыт, до тех пор пока у вас в списке есть другие токены этой сети. + + Токен %s является основной валютой в сети %s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети. Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. + Сеть не поддерживается. Пожалуйста, выберите другую сеть. + Сеть %s Бэкап кошелька не был произведен diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index 958f8f3c5e..daca6f30ab 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -64,6 +64,8 @@ The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. %s network not found. Please, add it first and try again. + This network is not supported. Please select another network. + %s network Your wallet has not been backed up diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f36d1cd4bf..09cdff0b35 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,10 +5,6 @@ @color/colorPrimary @color/colorSecondary @color/darkGray6 - true - true - true - @null @color/menu_accent_color @style/Widget.AppTheme.CardView diff --git a/app/src/main/res/values/zendesk_styles.xml b/app/src/main/res/values/zendesk_styles.xml index ebb4fcab6b..0022b241b1 100644 --- a/app/src/main/res/values/zendesk_styles.xml +++ b/app/src/main/res/values/zendesk_styles.xml @@ -4,4 +4,15 @@ @color/darkGray6 @color/darkGray6 + + // Contact us FAB style + diff --git a/dependencies.gradle b/dependencies.gradle index 45200c5dcf..b4a597ff72 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,12 +1,12 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', - tamgem_card_sdk : 'develop-154', - tamgem_blockchain_sdk: 'develop-93', + tamgem_card_sdk : 'develop-156', + tamgem_blockchain_sdk: 'develop-96', ] ext.environmentConfig = [ environment : "ENVIRONMENT", testActionEnabled: "TEST_ACTION_ENABLED", logEnabled : "LOG_ENABLED", -] \ No newline at end of file +] diff --git a/domain/build.gradle b/domain/build.gradle index d7a8832fd4..f4b0183cc8 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -85,4 +85,4 @@ dependencies { testImplementation "com.google.truth:truth:1.1.3" androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file +}