Updated on 2026-08-14

This commit is contained in:
Tangem 2021-06-18 12:30:18 +00:00
commit 8444c7aaae
13 changed files with 145 additions and 72 deletions

View file

@ -77,7 +77,7 @@ dependencies {
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'com.tangem:blockchain:develop-22'
implementation 'com.tangem:blockchain:develop-24'
implementation 'com.tangem:core:develop-36'
implementation 'com.tangem:sdk:develop-36'

View file

@ -6,6 +6,7 @@ import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber
class DialogManager : StoreSubscriber<GlobalState> {
@ -38,7 +39,18 @@ class DialogManager : StoreSubscriber<GlobalState> {
when (state.dialog) {
is WalletConnectDialog.UnsupportedCard ->
dialog = UnsupportedCardDialog.create(context)
dialog = SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
context = context
)
is WalletConnectDialog.OpeningSessionRejected -> {
dialog = SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_same_wcuri,
context = context
)
}
is WalletConnectDialog.ApproveWcSession ->
dialog = ApproveWcSessionDialog.create(state.dialog.session, context)
is WalletConnectDialog.ClipboardOrScanQr ->

View file

@ -100,9 +100,9 @@ class CurrenciesRepository(val context: Application) {
fun getBlockchains(cardFirmware: FirmwareVersion?): List<Blockchain> {
return if (cardFirmware == null || cardFirmware.major < 4) {
secp256k1Blochcains
secp256k1Blockchains
} else {
secp256k1Blochcains + ed25519Blockchains
secp256k1Blockchains + ed25519Blockchains
}
}
@ -115,10 +115,19 @@ class CurrenciesRepository(val context: Application) {
fun getFileNameForBlockchains(cardId: String): String =
"${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId"
private val secp256k1Blochcains = listOf(
Blockchain.Bitcoin, Blockchain.BitcoinCash, Blockchain.Binance, Blockchain.Litecoin,
Blockchain.XRP, Blockchain.Tezos,
Blockchain.Ethereum, Blockchain.RSK)
private val secp256k1Blockchains = listOf(
Blockchain.Bitcoin,
Blockchain.BitcoinCash,
Blockchain.Binance,
Blockchain.BSC,
Blockchain.Litecoin,
Blockchain.XRP,
Blockchain.Tezos,
Blockchain.Ethereum,
Blockchain.RSK,
Blockchain.Polygon,
Blockchain.Dogecoin,
)
private val ed25519Blockchains = listOf(Blockchain.CardanoShelley, Blockchain.Stellar)
}
}

View file

@ -44,6 +44,10 @@ class WalletConnectManager {
fun connect(wcUri: String, wallet: WalletForSession) {
val session = WCSession.from(wcUri) ?: return
if (sessions[session] != null) {
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
return
}
val client = WCClient(httpClient = okHttpClient)
setListeners(client)
val peerId = UUID.randomUUID().toString()

View file

@ -29,6 +29,8 @@ sealed class WalletConnectAction : Action {
val wallet: WalletForSession,
) : WalletConnectAction()
object RefuseOpeningSession : WalletConnectAction()
data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction()
data class ApproveSession(
@ -37,7 +39,7 @@ sealed class WalletConnectAction : Action {
data class Success(val session: WalletConnectSession) : WalletConnectAction()
}
object FailureEstablishingSession : WalletConnectAction()
data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction()
data class SetSessionsRestored(val sessions: List<WalletConnectSession>) :

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.*
import com.tangem.commands.common.card.Card
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.toHexString
import com.tangem.tap.*
@ -14,6 +15,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.isMultiwalletAllowed
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -55,52 +57,12 @@ class WalletConnectMiddleware {
}
is WalletConnectAction.ScanCard -> {
scope.launch {
val result = tangemSdkManager.scanNote(
FirebaseAnalyticsHandler,
R.string.wallet_connect_scan_card_message
)
withContext(Dispatchers.Main) {
when (result) {
is CompletionResult.Success -> {
val card = result.data.card
val factory =
store.state.globalState.tapWalletManager.walletManagerFactory
handleScanCard(action.wcUri)
}
val walletManager = if (card.isMultiwalletAllowed) {
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
Blockchain.Ethereum) == true
) {
factory.makeWalletManagerForApp(result.data.card,
Blockchain.Ethereum)
} else {
factory.makeWalletManagerForApp(result.data.card,
Blockchain.Ethereum)?.also {
currenciesRepository.saveAddedBlockchain(card.cardId,
Blockchain.Ethereum)
}
}
} else {
null
}
if (walletManager == null) {
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
return@withContext
};
val key = walletManager.wallet.publicKey
store.dispatchOnMain(WalletConnectAction.OpenSession(
wcUri = action.wcUri,
wallet = WalletForSession(
card.cardId, key.toHexString(),
isTestNet = false
),
))
}
is CompletionResult.Failure ->
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession)
}
}
is WalletConnectAction.FailureEstablishingSession -> {
if (action.session != null) {
walletConnectManager.disconnect(action.session)
}
}
@ -115,6 +77,12 @@ class WalletConnectMiddleware {
)
}
is WalletConnectAction.RefuseOpeningSession -> {
store.dispatch(GlobalAction.ShowDialog(
WalletConnectDialog.OpeningSessionRejected
))
}
is WalletConnectAction.AcceptOpeningSession -> {
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ApproveWcSession(
action.session)))
@ -157,4 +125,69 @@ class WalletConnectMiddleware {
}
}
}
private fun handleScanCard(wcUri: String) {
scope.launch {
val result = tangemSdkManager.scanNote(
FirebaseAnalyticsHandler,
R.string.wallet_connect_scan_card_message
)
withContext(Dispatchers.Main) {
when (result) {
is CompletionResult.Success -> {
val card = result.data.card
if (!card.isMultiwalletAllowed) {
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
return@withContext
}
val walletManager = getWalletManager(card)
if (walletManager == null) {
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
return@withContext
}
val key = walletManager.wallet.publicKey
store.dispatchOnMain(WalletConnectAction.OpenSession(
wcUri = wcUri,
wallet = WalletForSession(
card.cardId, key.toHexString(),
isTestNet = false
),
))
}
is CompletionResult.Failure ->
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(
null
))
}
}
}
}
private fun getWalletManager(card: Card): WalletManager? {
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
return if (store.state.globalState.scanNoteResponse?.card?.cardId == card.cardId) {
store.state.walletState.getWalletManager(Blockchain.Ethereum)
?: factory.makeWalletManagerForApp(card, Blockchain.Ethereum)
?.also { walletManager ->
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
}
} else {
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
Blockchain.Ethereum) == true
) {
factory.makeWalletManagerForApp(card, Blockchain.Ethereum)
} else {
factory.makeWalletManagerForApp(card,
Blockchain.Ethereum)
?.also {
currenciesRepository.saveAddedBlockchain(card.cardId, Blockchain.Ethereum)
}
}
}
}
}

View file

@ -25,8 +25,9 @@ class WalletConnectReducer {
state.copy(sessions = sessions)
}
is WalletConnectAction.ScanCard -> state.copy(loading = true)
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false)
is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false)
else -> state

View file

@ -41,6 +41,7 @@ data class WalletForSession(
sealed class WalletConnectDialog : StateDialog {
data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog()
object UnsupportedCard : WalletConnectDialog()
object OpeningSessionRejected : WalletConnectDialog()
data class ApproveWcSession(val session: WalletConnectSession) : WalletConnectDialog()
data class RequestTransaction(val dialogData: TransactionRequestDialogData) :
WalletConnectDialog()

View file

@ -25,7 +25,9 @@ class ApproveWcSessionDialog {
session.session
))
}
setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> }
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
}
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}

View file

@ -6,15 +6,19 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import com.tangem.wallet.R
class UnsupportedCardDialog {
class SimpleAlertDialog {
companion object {
fun create(context: Context): AlertDialog {
fun create(
titleRes: Int,
messageRes: Int,
buttonRes: Int = R.string.common_ok,
context: Context,
): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(context.getString(R.string.wallet_connect))
setMessage(context.getText(R.string.wallet_connect_scanner_error_no_ethereum_wallet))
setPositiveButton(context.getText(R.string.common_ok)) { _, _ -> }
// setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> }
setTitle(context.getString(titleRes))
setMessage(context.getText(messageRes))
setPositiveButton(context.getText(buttonRes)) { _, _ -> }
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}

View file

@ -14,9 +14,9 @@ import com.tangem.Log
import com.tangem.TangemSdkLogger
import com.tangem.blockchain.common.*
import com.tangem.commands.common.card.Card
import com.tangem.commands.wallet.WalletStatus
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.domain.TapWorkarounds
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.store
import timber.log.Timber
import java.io.File
@ -173,7 +173,9 @@ class AdditionalEmailInfo {
fun setCardInfo(card: Card) {
cardId = card.cardId
cardFirmwareVersion = card.firmwareVersion.version
signedHashesCount = card.signedHashesCount().toString()
signedHashesCount = card.wallets
.filter { it.status == WalletStatus.Loaded }
.joinToString(";") { "${it.curve?.curve} - ${it.signedHashes}" }
}
fun setWalletsInfo(walletManagers: List<WalletManager>) {
@ -289,6 +291,7 @@ class SendTransactionFailedEmail(private val error: String) : EmailData {
appendKeyValue("OS version", infoHolder.osVersion)
appendKeyValue("App version", infoHolder.appVersion)
appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion)
appendKeyValue("Signed hashes", infoHolder.signedHashesCount)
// appendKeyValue("Transaction HEX", infoHolder.transactionHex)
}.toString()
}
@ -301,6 +304,7 @@ class FeedbackEmail : EmailData {
val builder = StringBuilder()
builder.appendKeyValue("Card ID", infoHolder.cardId)
builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion)
builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount)
infoHolder.walletsInfo.forEach {
builder.appendKeyValue("Blockchain", it.blockchain.fullName)

View file

@ -31,14 +31,6 @@
android:layout_marginTop="16dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/pb_wallet_connect"
android:indeterminate="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv_no_sessions_title"
android:layout_width="wrap_content"
@ -71,6 +63,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/pb_wallet_connect"
android:indeterminate="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_open_session"
android:layout_width="wrap_content"

View file

@ -154,4 +154,6 @@ this wallet.
<string name="wallet_connect_paste_from_clipboard" translatable="false">Paste from clipboard</string>
<string name="wallet_connect_scan_new_code" translatable="false">Scan new code</string>
<string name="wallet_connect_scanner_error_no_ethereum_wallet" translatable="false">This card cant be used to establish WalletConnect session</string>
<string name="wallet_connect_could_not_complete" translatable="false">The operation couldn\'t be completed</string>
<string name="wallet_connect_same_wcuri" translatable="false">The operation couldn\'t be completed. \n\nYou have already established a WalletConnect session with this parameters.</string>
</resources>