Updated on 2026-08-14
This commit is contained in:
parent
0cce836c96
commit
00cf8c5b0c
34 changed files with 502 additions and 268 deletions
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
data class ByteArrayKey(val bytes: ByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other || other is ByteArrayKey && this.bytes.contentEquals(other.bytes)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = bytes.contentHashCode()
|
||||
}
|
||||
|
||||
fun ByteArray.toMapKey(): ByteArrayKey = ByteArrayKey(this)
|
||||
|
||||
fun ByteArrayKey.bytes(): ByteArray = this.bytes
|
||||
|
|
@ -36,6 +36,7 @@ sealed class GlobalAction : Action {
|
|||
}
|
||||
|
||||
data class ScanCard(
|
||||
val shouldDeriveWC: Boolean,
|
||||
val onSuccess: ((ScanResponse) -> Unit)? = null,
|
||||
val onFailure: ((TangemError) -> Unit)? = null,
|
||||
val messageResId: Int? = null,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.global
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
|
|
@ -13,10 +14,6 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
|||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.moonpay.MoonpayService
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
|
@ -91,7 +88,12 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
}
|
||||
is GlobalAction.ScanCard -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId)
|
||||
val result = tangemSdkManager.scanProduct(
|
||||
FirebaseAnalyticsHandler,
|
||||
currenciesRepository,
|
||||
action.shouldDeriveWC,
|
||||
action.messageResId
|
||||
)
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
|
||||
withMainContext {
|
||||
when (result) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import CreateProductWalletAndRescanTask
|
||||
import CreateProductWalletTask
|
||||
import android.content.Context
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
|
|
@ -12,7 +12,10 @@ import com.tangem.common.card.FirmwareVersion
|
|||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.Config
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeyList
|
||||
import com.tangem.operations.pins.CheckUserCodesCommand
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
|
|
@ -23,6 +26,7 @@ import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
|||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -33,18 +37,20 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
|
||||
suspend fun scanProduct(
|
||||
analyticsHandler: AnalyticsHandler,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
shouldDeriveWC: Boolean,
|
||||
messageRes: Int? = null,
|
||||
): CompletionResult<ScanResponse> {
|
||||
analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
|
||||
|
||||
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(ScanProductTask(), null, message)
|
||||
.also { sendScanFailuresToAnalytics(analyticsHandler, it) }
|
||||
return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository, shouldDeriveWC), null, message)
|
||||
.also { sendScanFailuresToAnalytics(analyticsHandler, it) }
|
||||
}
|
||||
|
||||
suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult<Card> {
|
||||
return runTaskAsync(
|
||||
CreateProductWalletAndRescanTask(scanResponse.productType),
|
||||
CreateProductWalletTask(scanResponse.productType),
|
||||
scanResponse.card.cardId,
|
||||
Message(context.getString(R.string.initial_message_create_wallet_body))
|
||||
)
|
||||
|
|
@ -66,6 +72,14 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)))
|
||||
}
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray,
|
||||
derivationPaths: List<DerivationPath>
|
||||
): CompletionResult<ExtendedPublicKeyList> {
|
||||
return runTaskAsyncReturnOnMain(DeriveWalletPublicKeysTask(walletPublicKey, derivationPaths), cardId)
|
||||
}
|
||||
|
||||
suspend fun resetToFactorySettings(card: Card): CompletionResult<Card> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
ResetToFactorySettingsTask(),
|
||||
|
|
@ -104,13 +118,13 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
suspend fun <T : CommandResponse> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
): CompletionResult<T> =
|
||||
withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
|
||||
continuation.resume(result)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T : CommandResponse> runTaskAsyncReturnOnMain(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.domain
|
|||
import com.tangem.Message
|
||||
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.tap.domain.tasks.SignHashesTask
|
||||
|
|
@ -15,13 +16,9 @@ class TangemSigner(
|
|||
private val signerCallback: (TangemSignerResponse) -> Unit,
|
||||
) : TransactionSigner {
|
||||
|
||||
override suspend fun sign(
|
||||
hash: ByteArray,
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray,
|
||||
): CompletionResult<ByteArray> =
|
||||
suspendCoroutine { continuation ->
|
||||
val command = SignHashTask(hash, walletPublicKey)
|
||||
override suspend fun sign(hash: ByteArray, cardId: String, publicKey: Wallet.PublicKey): CompletionResult<ByteArray> {
|
||||
return suspendCoroutine { continuation ->
|
||||
val command = SignHashTask(hash, publicKey.seedKey)
|
||||
tangemSdk.startSessionWithRunnable(
|
||||
runnable = command,
|
||||
cardId = cardId,
|
||||
|
|
@ -42,15 +39,11 @@ class TangemSigner(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun sign(
|
||||
hashes: List<ByteArray>,
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray,
|
||||
): CompletionResult<List<ByteArray>> =
|
||||
suspendCoroutine { continuation ->
|
||||
val task = SignHashesTask(hashes, walletPublicKey)
|
||||
override suspend fun sign(hashes: List<ByteArray>, cardId: String, publicKey: Wallet.PublicKey): CompletionResult<List<ByteArray>> {
|
||||
return suspendCoroutine { continuation ->
|
||||
val task = SignHashesTask(hashes, publicKey.seedKey)
|
||||
tangemSdk.startSessionWithRunnable(
|
||||
runnable = task,
|
||||
cardId = cardId,
|
||||
|
|
@ -71,6 +64,7 @@ class TangemSigner(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TangemSignerResponse(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
|
|
@ -131,7 +130,7 @@ class TapWalletManager {
|
|||
store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, primaryWalletManager)
|
||||
loadMultiWalletData(data, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain)))
|
||||
|
|
@ -139,7 +138,7 @@ class TapWalletManager {
|
|||
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, null)
|
||||
loadMultiWalletData(data, blockchain, null)
|
||||
}
|
||||
}
|
||||
val moonPayStatus = store.state.globalState.moonpayStatus
|
||||
|
|
@ -149,10 +148,10 @@ class TapWalletManager {
|
|||
}
|
||||
|
||||
private fun loadMultiWalletData(
|
||||
card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
scanResponse: ScanResponse, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
) {
|
||||
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
|
||||
val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
|
||||
val savedCurrencies = currenciesRepository.loadCardCurrencies(scanResponse.card.cardId)
|
||||
|
||||
if (savedCurrencies == null) {
|
||||
if (primaryBlockchain != null && primaryWalletManager != null) {
|
||||
|
|
@ -169,11 +168,11 @@ class TapWalletManager {
|
|||
CardCurrencies(blockchains = blockchains, tokens = emptyList())
|
||||
))
|
||||
val walletManagers =
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList())
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains.toList())
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains.toList()))
|
||||
}
|
||||
store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse(card, walletManagerFactory))
|
||||
store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse)
|
||||
store.dispatch(WalletAction.MultiWallet.FindTokensInUse)
|
||||
} else {
|
||||
val blockchains = savedCurrencies.blockchains.toList()
|
||||
|
|
@ -182,10 +181,10 @@ class TapWalletManager {
|
|||
primaryWalletManager != null && primaryBlockchain != null
|
||||
) {
|
||||
val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain }
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchainsWithoutPrimary)
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchainsWithoutPrimary)
|
||||
.plus(primaryWalletManager)
|
||||
} else {
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchains)
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
|
||||
}
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
|
|
|
|||
|
|
@ -3,74 +3,70 @@ package com.tangem.tap.domain.extensions
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
card: Card,
|
||||
blockchain: Blockchain,
|
||||
scanResponse: ScanResponse, blockchain: Blockchain
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
if (card.isTestCard && blockchain.getTestnetVersion() == null) return null
|
||||
val supportedCurves = blockchain.getSupportedCurves() ?: return null
|
||||
|
||||
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
|
||||
val wallet = selectWallet(wallets)
|
||||
val publicKey = wallet?.publicKey ?: return null
|
||||
val curveToUse = wallet.curve ?: return null
|
||||
return if (card.isTestCard) {
|
||||
blockchain.getTestnetVersion()?.let {
|
||||
makeWalletManager(card.cardId, publicKey, it, curveToUse)
|
||||
val wallet = selectWallet(wallets) ?: return null
|
||||
|
||||
val environmentBlockchain = if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
|
||||
|
||||
val seedKey = wallet.extendedPublicKey
|
||||
return when {
|
||||
scanResponse.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
|
||||
makeTwinWalletManager(
|
||||
card.cardId,
|
||||
wallet.publicKey, scanResponse.secondTwinPublicKey.hexToBytes(),
|
||||
environmentBlockchain, wallet.curve
|
||||
)
|
||||
}
|
||||
seedKey != null -> {
|
||||
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
|
||||
val derivedKey = derivedKeys?.firstOrNull { it.derivationPath == blockchain.derivationPath() }
|
||||
?: return null
|
||||
|
||||
makeWalletManager(card.cardId, environmentBlockchain, seedKey, derivedKey)
|
||||
}
|
||||
else -> {
|
||||
makeWalletManager(card.cardId, environmentBlockchain, wallet.publicKey, wallet.curve)
|
||||
}
|
||||
} else {
|
||||
makeWalletManager(card.cardId, publicKey, blockchain, curveToUse)
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse: ScanResponse, blockchains: List<Blockchain>,
|
||||
): List<WalletManager> {
|
||||
val isTestCard = scanResponse.card.isTestCard
|
||||
val filteredBlockchains = blockchains.mapNotNull { if (isTestCard) it.getTestnetVersion() else it }
|
||||
return filteredBlockchains.mapNotNull { makeWalletManagerForApp(scanResponse, it) }
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makePrimaryWalletManager(
|
||||
scanResponse: ScanResponse,
|
||||
): WalletManager? {
|
||||
val blockchain = if (scanResponse.card.isTestCard) {
|
||||
scanResponse.getBlockchain().getTestnetVersion() ?: return null
|
||||
} else {
|
||||
scanResponse.getBlockchain()
|
||||
}
|
||||
return makeWalletManagerForApp(scanResponse, blockchain)
|
||||
}
|
||||
|
||||
private fun selectWallet(wallets: List<CardWallet>): CardWallet? {
|
||||
return when (wallets.size) {
|
||||
0 -> null
|
||||
1 -> wallets[0]
|
||||
else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0]
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagersForApp(
|
||||
card: Card, blockchains: List<Blockchain>,
|
||||
): List<WalletManager> {
|
||||
return if (card.isTestCard) {
|
||||
blockchains.mapNotNull { blockchain ->
|
||||
blockchain.getTestnetVersion()?.let { makeWalletManagerForApp(card, it) }
|
||||
}
|
||||
} else {
|
||||
blockchains.mapNotNull { blockchain -> makeWalletManagerForApp(card, blockchain) }
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makePrimaryWalletManager(
|
||||
data: ScanResponse,
|
||||
): WalletManager? {
|
||||
val card = data.card
|
||||
val blockchain = if (card.isTestCard) {
|
||||
data.getBlockchain().getTestnetVersion()
|
||||
} else {
|
||||
data.getBlockchain()
|
||||
}
|
||||
val supportedCurves = blockchain?.getSupportedCurves() ?: return null
|
||||
|
||||
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
|
||||
val wallet = selectWallet(wallets)
|
||||
val publicKey = wallet?.publicKey ?: return null
|
||||
val curveToUse = wallet.curve
|
||||
|
||||
return if (data.isTangemTwins() && data.secondTwinPublicKey != null) {
|
||||
makeMultisigWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(),
|
||||
blockchain = blockchain, curve = curveToUse
|
||||
)
|
||||
} else {
|
||||
makeWalletManager(card.cardId, publicKey, blockchain, curveToUse)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.operations.PreflightReadMode
|
|||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.wallet.CreateWalletCommand
|
||||
|
||||
@Deprecated("Use CreateProductWalletsResponse instead")
|
||||
@Deprecated("Use CreateProductWalletTask instead")
|
||||
class CreateWalletsTask(curves: List<EllipticCurve>? = null) : CardSessionRunnable<Card> {
|
||||
|
||||
private val curves = curves ?: listOf(
|
||||
|
|
|
|||
|
|
@ -1,25 +1,29 @@
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeyList
|
||||
import com.tangem.operations.wallet.CreateWalletCommand
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletsTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
|
||||
import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
|
||||
import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CreateProductWalletAndRescanTask(
|
||||
class CreateProductWalletTask(
|
||||
private val type: ProductType,
|
||||
) : CardSessionRunnable<Card> {
|
||||
|
||||
|
|
@ -28,33 +32,19 @@ class CreateProductWalletAndRescanTask(
|
|||
callback(CompletionResult.Failure(TangemSdkError.CardError()))
|
||||
return
|
||||
}
|
||||
CreateProductWalletTask(card, type).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> PreflightReadTask(PreflightReadMode.FullCardRead).run(
|
||||
session,
|
||||
callback)
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CreateProductWalletTask(
|
||||
private val card: Card,
|
||||
private val type: ProductType,
|
||||
) : CardSessionRunnable<CreateWalletResponse> {
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateWalletResponse>) -> Unit,
|
||||
) {
|
||||
val commandProcessor = when (type) {
|
||||
ProductType.Note -> CreateWalletTangemNote()
|
||||
ProductType.Twins -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
|
||||
ProductType.Wallet -> CreateWalletTangemWallet()
|
||||
else -> CreateWalletOtherCards()
|
||||
}
|
||||
commandProcessor.proceed(card, session, callback)
|
||||
commandProcessor.proceed(card, session) {
|
||||
when (it) {
|
||||
is CompletionResult.Success -> callback(CompletionResult.Success(session.environment.card!!))
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,12 +75,12 @@ private class CreateWalletTangemNote : ProductCommandProcessor<CreateWalletRespo
|
|||
intersectCurves[0]
|
||||
}
|
||||
CreateWalletCommand(curve).run(session, callback)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CreateWalletTangemWallet : ProductCommandProcessor<CreateWalletResponse> {
|
||||
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
|
|
@ -98,16 +88,37 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateWalletRes
|
|||
) {
|
||||
val supportedCurves = setOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519)
|
||||
val curves = card.getCurvesForNonCreatedWallets().intersect(supportedCurves).toList()
|
||||
CreateProductWalletsTask(curves).run(session) { result ->
|
||||
CreateWalletsTask(curves).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
callback(CompletionResult.Success(result.data.createWalletResponses[0]))
|
||||
is CompletionResult.Success -> {
|
||||
val response = result.data.createWalletResponses[0]
|
||||
val derivationPaths = listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
.mapNotNull { it.derivationPath() }
|
||||
|
||||
DeriveWalletPublicKeysTask(response.wallet.publicKey, derivationPaths).run(session) {
|
||||
when (it) {
|
||||
is CompletionResult.Success -> {
|
||||
updateDerivedKeys(response.wallet, it.data)
|
||||
callback(CompletionResult.Success(response))
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: updating derived wallet public keys - make it better later
|
||||
private fun updateDerivedKeys(wallet: CardWallet, derivedKeys: ExtendedPublicKeyList) {
|
||||
val onboardingManager = store.state.globalState.onboardingManager ?: return
|
||||
onboardingManager.scanResponse = onboardingManager.scanResponse.copy(
|
||||
derivedKeys = mapOf(wallet.publicKey.toMapKey() to derivedKeys)
|
||||
)
|
||||
}
|
||||
|
||||
private class CreateWalletOtherCards : ProductCommandProcessor<CreateWalletResponse> {
|
||||
|
||||
override fun proceed(
|
||||
|
|
@ -117,9 +128,9 @@ private class CreateWalletOtherCards : ProductCommandProcessor<CreateWalletRespo
|
|||
) {
|
||||
val firmwareVersion = card.firmwareVersion
|
||||
val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
|
||||
CreateProductWalletsTask(listOf(card.supportedCurves.first()))
|
||||
CreateWalletsTask(listOf(card.supportedCurves.first()))
|
||||
} else {
|
||||
CreateProductWalletsTask(card.getCurvesForNonCreatedWallets())
|
||||
CreateWalletsTask(card.getCurvesForNonCreatedWallets())
|
||||
}
|
||||
|
||||
task.run(session) { result ->
|
||||
|
|
@ -11,17 +11,17 @@ import com.tangem.operations.wallet.CreateWalletResponse
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CreateProductWalletsResponse(
|
||||
class CreateWalletsResponse(
|
||||
val createWalletResponses: List<CreateWalletResponse>
|
||||
) : CommandResponse
|
||||
|
||||
class CreateProductWalletsTask(
|
||||
class CreateWalletsTask(
|
||||
private val curves: List<EllipticCurve>,
|
||||
) : CardSessionRunnable<CreateProductWalletsResponse> {
|
||||
) : CardSessionRunnable<CreateWalletsResponse> {
|
||||
|
||||
private val createdWalletsResponses = mutableListOf<CreateWalletResponse>()
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateProductWalletsResponse>) -> Unit) {
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit) {
|
||||
val curve = curves[createdWalletsResponses.size]
|
||||
createWallet(curve, session, callback)
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ class CreateProductWalletsTask(
|
|||
private fun createWallet(
|
||||
curve: EllipticCurve,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateProductWalletsResponse>) -> Unit
|
||||
callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit
|
||||
) {
|
||||
|
||||
CreateWalletCommand(curve).run(session) { result ->
|
||||
|
|
@ -37,7 +37,7 @@ class CreateProductWalletsTask(
|
|||
is CompletionResult.Success -> {
|
||||
createdWalletsResponses.add(result.data)
|
||||
if (createdWalletsResponses.size == curves.size) {
|
||||
callback(CompletionResult.Success(CreateProductWalletsResponse(createdWalletsResponses)))
|
||||
callback(CompletionResult.Success(CreateWalletsResponse(createdWalletsResponses)))
|
||||
return@run
|
||||
}
|
||||
createWallet(curves[createdWalletsResponses.size], session, callback)
|
||||
|
|
@ -14,11 +14,16 @@ import com.tangem.common.core.TangemError
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.hdWallet.ExtendedPublicKey
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.TapWorkarounds
|
||||
|
|
@ -26,6 +31,7 @@ import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
|||
import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
||||
import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.domain.twins.TwinsHelper
|
||||
|
||||
data class ScanResponse(
|
||||
|
|
@ -33,6 +39,7 @@ data class ScanResponse(
|
|||
val productType: ProductType,
|
||||
val walletData: WalletData?,
|
||||
val secondTwinPublicKey: String? = null,
|
||||
val derivedKeys: Map<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf()
|
||||
) : CommandResponse {
|
||||
|
||||
fun getBlockchain(): Blockchain {
|
||||
|
|
@ -60,7 +67,13 @@ data class ScanResponse(
|
|||
fun twinsIsTwinned(): Boolean = card.isTangemTwins() && walletData != null && secondTwinPublicKey != null
|
||||
}
|
||||
|
||||
class ScanProductTask(val card: Card? = null) : CardSessionRunnable<ScanResponse> {
|
||||
typealias KeyWalletPublicKey = ByteArrayKey
|
||||
|
||||
class ScanProductTask(
|
||||
val card: Card? = null,
|
||||
private val currenciesRepository: CurrenciesRepository?,
|
||||
private val shouldDeriveWC: Boolean
|
||||
) : CardSessionRunnable<ScanResponse> {
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
|
|
@ -80,7 +93,7 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable<ScanResponse
|
|||
val commandProcessor = when {
|
||||
TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor()
|
||||
card.isTangemTwins() -> ScanTwinProcessor()
|
||||
TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor()
|
||||
TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor(currenciesRepository, shouldDeriveWC)
|
||||
else -> ScanOtherCardsProcessor()
|
||||
}
|
||||
commandProcessor.proceed(card, session) { processorResult ->
|
||||
|
|
@ -116,13 +129,60 @@ private class ScanNoteProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
}
|
||||
}
|
||||
|
||||
private class ScanWalletProcessor : ProductCommandProcessor<ScanResponse> {
|
||||
private class ScanWalletProcessor(
|
||||
private val currenciesRepository: CurrenciesRepository?,
|
||||
private val shouldDeriveWC: Boolean
|
||||
) : ProductCommandProcessor<ScanResponse> {
|
||||
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData)))
|
||||
val derivationPaths = collectDerivationPaths(card)
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
|
||||
|
||||
if (derivationPaths.isNullOrEmpty() || wallet == null || wallet.chainCode == null) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData)))
|
||||
return
|
||||
}
|
||||
|
||||
DeriveWalletPublicKeysTask(wallet.publicKey, derivationPaths).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val derivedKeys = mapOf(wallet.publicKey.toMapKey() to result.data)
|
||||
val response = ScanResponse(
|
||||
card,
|
||||
ProductType.Wallet,
|
||||
session.environment.walletData,
|
||||
derivedKeys = derivedKeys
|
||||
)
|
||||
callback(CompletionResult.Success(response))
|
||||
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectDerivationPaths(card: Card): List<DerivationPath>? {
|
||||
val currenciesRepository = currenciesRepository ?: return null
|
||||
val cardCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
|
||||
|
||||
val blockchainsToDerive = if (cardCurrencies == null) {
|
||||
mutableListOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
} else {
|
||||
val tokenBlockchains = cardCurrencies.tokens.map { it.blockchain }
|
||||
(cardCurrencies.blockchains + tokenBlockchains).toMutableList()
|
||||
}
|
||||
|
||||
if (shouldDeriveWC) {
|
||||
blockchainsToDerive.addAll(listOf(Blockchain.Ethereum, Blockchain.Binance, Blockchain.EthereumTestnet))
|
||||
}
|
||||
|
||||
return blockchainsToDerive.toSet()
|
||||
.filter { it.getSupportedCurves()?.contains(EllipticCurve.Secp256k1) == true }
|
||||
.mapNotNull { it.derivationPath() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +234,7 @@ private class ScanOtherCardsProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
return
|
||||
}
|
||||
|
||||
CreateProductWalletsTask(curvesToCreate).run(session) { result ->
|
||||
CreateWalletsTask(curvesToCreate).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
PreflightReadTask(PreflightReadMode.FullCardRead, card.cardId).run(session) { readResult ->
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class TopUpManager {
|
|||
store.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey,
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
remainingSignatures = signResponse.remainingSignatures
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class FinalizeTwinTask(
|
|||
PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success ->
|
||||
ScanProductTask(readResult.data).run(session, callback)
|
||||
ScanProductTask(readResult.data, null, false).run(session, callback)
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Failure(readResult.error))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.domain.walletconnect
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -97,10 +96,9 @@ class WalletConnectManager {
|
|||
val activeData = sessions[session] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
val accounts = listOf(Blockchain.Ethereum.makeAddresses(activeData.wallet.walletPublicKey).first().value)
|
||||
val approved = activeData.client.approveSession(
|
||||
accounts = listOf(Blockchain.Ethereum.makeAddresses(
|
||||
activeData.wallet.walletPublicKey.hexToBytes()).first().value
|
||||
),
|
||||
accounts = accounts,
|
||||
chainId = activeData.wallet.chainId
|
||||
)
|
||||
if (approved) {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ 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.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.*
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
|
@ -36,9 +33,14 @@ class WalletConnectSdkHelper {
|
|||
type: WcTransactionType,
|
||||
): WcTransactionData? {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val publicKey = Wallet.PublicKey(
|
||||
session.wallet.walletPublicKey,
|
||||
session.wallet.derivedPublicKey,
|
||||
session.wallet.derivationPath,
|
||||
)
|
||||
val walletManager = factory.makeEthereumWalletManager(
|
||||
session.wallet.cardId,
|
||||
session.wallet.walletPublicKey.hexToBytes(),
|
||||
publicKey,
|
||||
emptyList(),
|
||||
isTestNet = session.wallet.isTestNet
|
||||
) ?: return null
|
||||
|
|
@ -151,7 +153,7 @@ class WalletConnectSdkHelper {
|
|||
|
||||
val command = SignHashCommand(
|
||||
hash = dataToSign.hash,
|
||||
walletPublicKey = data.walletManager.wallet.publicKey
|
||||
walletPublicKey = data.walletManager.wallet.publicKey.seedKey
|
||||
)
|
||||
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
|
||||
return when (result) {
|
||||
|
|
@ -208,7 +210,7 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
|
||||
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
|
||||
val publicKey = wallet.walletPublicKey.hexToBytes()
|
||||
val publicKey = wallet.walletPublicKey
|
||||
val command = SignHashCommand(hashToSign, publicKey)
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
|
|
@ -14,6 +12,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.isMultiwalletAllowed
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -128,24 +127,32 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
|
||||
private fun handleScanCard(wcUri: String) {
|
||||
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
|
||||
store.dispatch(GlobalAction.ScanCard(true, { scanResponse ->
|
||||
val card = scanResponse.card
|
||||
if (!card.isMultiwalletAllowed) {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return@ScanCard
|
||||
}
|
||||
|
||||
val walletManager = getWalletManager(card).guard {
|
||||
val walletManager = getWalletManager(scanResponse).guard {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return@ScanCard
|
||||
}
|
||||
|
||||
val key = walletManager.wallet.publicKey
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey = if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(
|
||||
wcUri = wcUri,
|
||||
wallet = WalletForSession(
|
||||
card.cardId, key.toHexString(),
|
||||
isTestNet = card.isTestCard
|
||||
card.cardId,
|
||||
wallet.publicKey.seedKey,
|
||||
derivedKey,
|
||||
wallet.publicKey.derivationPath,
|
||||
card.isTestCard
|
||||
),
|
||||
))
|
||||
}, {
|
||||
|
|
@ -153,12 +160,13 @@ class WalletConnectMiddleware {
|
|||
}, R.string.wallet_connect_scan_card_message))
|
||||
}
|
||||
|
||||
private fun getWalletManager(card: Card): WalletManager? {
|
||||
private fun getWalletManager(scanResponse: ScanResponse): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
|
||||
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
|
||||
store.state.walletState.getWalletManager(Blockchain.Ethereum)
|
||||
?: factory.makeWalletManagerForApp(card, Blockchain.Ethereum)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
|
|
@ -167,10 +175,9 @@ class WalletConnectMiddleware {
|
|||
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
|
||||
Blockchain.Ethereum) == true
|
||||
) {
|
||||
factory.makeWalletManagerForApp(card, Blockchain.Ethereum)
|
||||
factory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
} else {
|
||||
factory.makeWalletManagerForApp(card,
|
||||
Blockchain.Ethereum)
|
||||
factory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
?.also {
|
||||
currenciesRepository.saveAddedBlockchain(card.cardId, Blockchain.Ethereum)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
|
||||
|
|
@ -21,9 +23,12 @@ data class WalletConnectSession(
|
|||
val peerMeta: WCPeerMeta,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class WalletForSession(
|
||||
val cardId: String,
|
||||
val walletPublicKey: String,
|
||||
val walletPublicKey: ByteArray,
|
||||
val derivedPublicKey: ByteArray?,
|
||||
val derivationPath: DerivationPath?,
|
||||
val isTestNet: Boolean = false,
|
||||
) {
|
||||
val chainId
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class HomeMiddleware {
|
|||
val handler = homeMiddleware
|
||||
|
||||
const val CARD_SHOP_URI =
|
||||
"https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate"
|
||||
"https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ private fun handleReadCard() {
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
} else {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
|
||||
store.dispatch(GlobalAction.ScanCard(false, { scanResponse ->
|
||||
store.state.globalState.tapWalletManager.updateConfigManager(scanResponse)
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ private fun handleWalletAction(action: Action) {
|
|||
withMainContext {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val updatedResponse = scanResponse.copy(card = result.data)
|
||||
//here we must use updated scanResponse after createWallet & derivation
|
||||
val updatedResponse = globalState.onboardingManager.scanResponse.copy(card = result.data)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
store.dispatch(OnboardingWalletAction.ProceedBackup)
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ private fun sendTransaction(
|
|||
store.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey,
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
remainingSignatures = signResponse.remainingSignatures
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@ import com.tangem.blockchain.common.Token
|
|||
import com.tangem.wallet.R
|
||||
|
||||
sealed class CurrencyListItem {
|
||||
var isAdded: Boolean = false
|
||||
var isLock: Boolean = false
|
||||
|
||||
data class TokenListItem(val token: Token) : CurrencyListItem()
|
||||
data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem()
|
||||
|
||||
data class TitleListItem(
|
||||
@StringRes val titleResId: Int,
|
||||
var isContentShown: Boolean = true,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import org.rekotlin.Action
|
|||
|
||||
sealed class TokensAction : Action {
|
||||
|
||||
object ResetState : TokensAction()
|
||||
|
||||
object LoadCurrencies : TokensAction() {
|
||||
data class Success(val currencies: List<CurrencyListItem>) : TokensAction()
|
||||
}
|
||||
|
|
@ -19,4 +21,9 @@ sealed class TokensAction : Action {
|
|||
|
||||
data class ToggleShowTokensForBlockchain(val isShown: Boolean, val blockchain: Blockchain) : TokensAction()
|
||||
|
||||
sealed class TokensList : TokensAction() {
|
||||
data class AddBlockchain(val blockchain: Blockchain) : TokensList()
|
||||
data class AddToken(val token: Token) : TokensList()
|
||||
object SaveChanges : TokensList()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,22 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.currenciesRepository
|
||||
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class TokensMiddleware {
|
||||
|
|
@ -12,22 +25,92 @@ class TokensMiddleware {
|
|||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is TokensAction.LoadCurrencies -> {
|
||||
val card = state()?.globalState?.scanResponse?.card
|
||||
val isTestcard = card?.isTestCard ?: false
|
||||
val tokens = currenciesRepository.getPopularTokens(isTestcard)
|
||||
val blockchains = currenciesRepository.getBlockchains(
|
||||
cardFirmware = card?.firmwareVersion,
|
||||
isTestNet = isTestcard
|
||||
)
|
||||
val currencies = CurrencyListItem.createListOfCurrencies(
|
||||
blockchains, tokens
|
||||
)
|
||||
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
|
||||
}
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies(action)
|
||||
is TokensAction.TokensList.SaveChanges -> handleSaveChanges(action)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
val isTestcard = scanResponse.card.isTestCard
|
||||
|
||||
val tokens = currenciesRepository.getPopularTokens(isTestcard)
|
||||
val blockchains = currenciesRepository.getBlockchains(
|
||||
cardFirmware = scanResponse.card.firmwareVersion,
|
||||
isTestNet = isTestcard
|
||||
)
|
||||
val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList()
|
||||
if (scanResponse.isTangemWallet()) {
|
||||
currencies.forEach {
|
||||
when (it) {
|
||||
is CurrencyListItem.TitleListItem -> {
|
||||
}
|
||||
is CurrencyListItem.BlockchainListItem -> {
|
||||
val firstCurve = it.blockchain.getSupportedCurves()?.firstOrNull()
|
||||
if (firstCurve == EllipticCurve.Ed25519) {
|
||||
it.isLock = true
|
||||
}
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
val firstCurve = it.token.blockchain.getSupportedCurves()?.firstOrNull()
|
||||
if (firstCurve == EllipticCurve.Ed25519) {
|
||||
it.isLock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
|
||||
}
|
||||
|
||||
private fun handleSaveChanges(action: TokensAction.TokensList.SaveChanges) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: return
|
||||
|
||||
val candidatesToAdd = store.state.tokensState.candidateToAdd
|
||||
if (candidatesToAdd.isEmpty()) return
|
||||
|
||||
val blockchains = candidatesToAdd.filterIsInstance<CurrencyListItem.BlockchainListItem>()
|
||||
.map { it.blockchain }
|
||||
val tokens = candidatesToAdd.filterIsInstance<CurrencyListItem.TokenListItem>()
|
||||
.map { it.token }
|
||||
if (blockchains.isEmpty() && tokens.isEmpty()) return
|
||||
|
||||
val derivationPathsCandidates = (blockchains + tokens.map { it.blockchain })
|
||||
.distinct().mapNotNull { it.derivationPath() }
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys = scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf()
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath }
|
||||
|
||||
val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.derivePublicKeys(scanResponse.card.cardId, wallet.publicKey, toDerive)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val newDerivedKeys = result.data
|
||||
alreadyDerivedKeys.addAll(newDerivedKeys)
|
||||
val updatedScanResponse = scanResponse.copy(
|
||||
derivedKeys = mapOf(mapKeyOfWalletPublicKey to alreadyDerivedKeys.toList())
|
||||
)
|
||||
|
||||
store.dispatch(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
|
||||
|
||||
val actions = blockchains.map { WalletAction.MultiWallet.AddBlockchain(it) } + tokens.map {
|
||||
WalletAction.MultiWallet.AddToken(it)
|
||||
}
|
||||
actions.forEach { store.dispatch(it) }
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
store.dispatchErrorNotification(TapError.CustomError("Error adding tokens"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
|
|||
|
||||
val tokensState = state.tokensState
|
||||
return when (action) {
|
||||
is TokensAction.ResetState -> TokensState()
|
||||
is TokensAction.LoadCurrencies.Success -> {
|
||||
tokensState.copy(currencies = action.currencies, shownCurrencies = action.currencies)
|
||||
}
|
||||
|
|
@ -33,15 +34,25 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
|
|||
if (action.isShown) {
|
||||
val shownCurrencies = tokensState.shownCurrencies
|
||||
.removeTokensForBlockchain(action.blockchain)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
tokensState.copy(shownCurrencies = shownCurrencies)
|
||||
} else {
|
||||
val shownCurrencies = tokensState.shownCurrencies
|
||||
.addTokensForBlockchain(action.blockchain, tokensState.currencies)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
tokensState.copy(shownCurrencies = shownCurrencies)
|
||||
}
|
||||
}
|
||||
is TokensAction.TokensList.AddBlockchain -> {
|
||||
val candidates = tokensState.candidateToAdd.toMutableSet()
|
||||
candidates.add(CurrencyListItem.BlockchainListItem(action.blockchain))
|
||||
tokensState.copy(candidateToAdd = candidates.toList())
|
||||
}
|
||||
is TokensAction.TokensList.AddToken -> {
|
||||
val candidates = tokensState.candidateToAdd.toMutableSet()
|
||||
candidates.add(CurrencyListItem.TokenListItem(action.token))
|
||||
tokensState.copy(candidateToAdd = candidates.toList())
|
||||
}
|
||||
else -> tokensState
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ data class TokensState(
|
|||
val addedCurrencies: CardCurrencies? = null,
|
||||
val currencies: List<CurrencyListItem> = emptyList(),
|
||||
val shownCurrencies: List<CurrencyListItem> = emptyList(),
|
||||
val candidateToAdd: List<CurrencyListItem> = emptyList(),
|
||||
) : StateType
|
||||
|
||||
data class TokenWithAmount(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.tokens.redux.TokensState
|
||||
import com.tangem.tap.features.tokens.ui.adapters.CurrenciesAdapter
|
||||
import com.tangem.tap.mainScope
|
||||
|
|
@ -57,30 +58,31 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
|
||||
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
|
||||
btn_tokens_save_changes.setOnClickListener { store.dispatch(TokensAction.TokensList.SaveChanges)}
|
||||
setupPopularTokensRecyclerView()
|
||||
}
|
||||
|
||||
|
||||
private fun setupPopularTokensRecyclerView() {
|
||||
viewAdapter = CurrenciesAdapter()
|
||||
rv_popular_tokens.layoutManager = LinearLayoutManager(context)
|
||||
rv_popular_tokens.adapter = viewAdapter
|
||||
}
|
||||
|
||||
|
||||
override fun newState(state: TokensState) {
|
||||
if (activity == null) return
|
||||
|
||||
btn_tokens_save_changes.isEnabled = state.candidateToAdd.isNotEmpty()
|
||||
|
||||
viewAdapter.addedCurrencies = state.addedCurrencies
|
||||
viewAdapter.submitUnfilteredList(state.shownCurrencies)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.menu_search -> {
|
||||
true
|
||||
}
|
||||
R.id.menu_search -> true
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
|
@ -101,6 +103,11 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
|
|||
.launchIn(mainScope)
|
||||
return super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
store.dispatch(TokensAction.ResetState)
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
fun SearchView.inputtedTextAsFlow(): Flow<String> = callbackFlow {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.tap.common.extensions.show
|
|||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.features.tokens.redux.CurrencyListItem
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.item_currency_subtitle.view.*
|
||||
|
|
@ -86,15 +85,15 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
is CurrencyListItem.BlockchainListItem -> {
|
||||
element.blockchain.currency.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized) ||
|
||||
element.blockchain.fullName.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
element.blockchain.fullName.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
element.token.name.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized) ||
|
||||
element.token.symbol.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
element.token.symbol.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
}
|
||||
is CurrencyListItem.TitleListItem -> true
|
||||
}
|
||||
|
|
@ -105,17 +104,16 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
submitList(list)
|
||||
}
|
||||
|
||||
class CurrenciesViewHolder(val view: View) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
class CurrenciesViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(currency: CurrencyListItem, addedCurrencies: CardCurrencies?) {
|
||||
when (currency) {
|
||||
is CurrencyListItem.BlockchainListItem -> {
|
||||
val blockchain = currency.blockchain
|
||||
view.tv_currency_name.text = blockchain.fullName
|
||||
view.tv_currency_symbol.text = blockchain.currency
|
||||
val isAdded = addedCurrencies?.blockchains?.contains(blockchain) == true
|
||||
view.btn_add_token.show(!isAdded)
|
||||
view.btn_token_added.show(isAdded)
|
||||
currency.isAdded = addedCurrencies?.blockchains?.contains(blockchain) == true
|
||||
modifyAddTokenButton(currency)
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = view.iv_currency,
|
||||
|
|
@ -124,9 +122,9 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
)
|
||||
|
||||
view.btn_add_token.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain))
|
||||
view.btn_add_token.hide()
|
||||
view.btn_token_added.show()
|
||||
store.dispatch(TokensAction.TokensList.AddBlockchain(blockchain))
|
||||
currency.isAdded = !currency.isAdded
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
|
|
@ -134,13 +132,11 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
view.tv_currency_name.text = token.name
|
||||
view.tv_currency_symbol.text = token.symbol
|
||||
|
||||
val isAdded = addedCurrencies?.tokens
|
||||
currency.isAdded = addedCurrencies?.tokens
|
||||
?.any {
|
||||
it.symbol == token.symbol && it.contractAddress == token.contractAddress
|
||||
} == true
|
||||
|
||||
view.btn_add_token.show(!isAdded)
|
||||
view.btn_token_added.show(isAdded)
|
||||
modifyAddTokenButton(currency)
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = view.iv_currency,
|
||||
|
|
@ -148,13 +144,23 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
token = token, blockchain = token.blockchain
|
||||
)
|
||||
view.btn_add_token.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.AddToken(token))
|
||||
view.btn_add_token.hide()
|
||||
view.btn_token_added.show()
|
||||
store.dispatch(TokensAction.TokensList.AddToken(token))
|
||||
currency.isAdded = !currency.isAdded
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun modifyAddTokenButton(currency: CurrencyListItem) {
|
||||
if (currency.isLock) {
|
||||
view.btn_add_token.isEnabled = false
|
||||
} else {
|
||||
val text = if (currency.isAdded) R.string.add_token_added else R.string.common_add
|
||||
view.btn_add_token.setText(text)
|
||||
view.btn_add_token.isEnabled = !currency.isAdded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TitleViewHolder(val view: View) :
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ sealed class WalletAction : Action {
|
|||
data class AddToken(val token: Token) : MultiWallet()
|
||||
data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet()
|
||||
object FindTokensInUse : MultiWallet()
|
||||
data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) :
|
||||
MultiWallet()
|
||||
object FindBlockchainsInUse : MultiWallet()
|
||||
|
||||
data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet()
|
||||
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ class MultiWalletMiddleware {
|
|||
fun handle(
|
||||
action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?,
|
||||
) {
|
||||
val globalState = globalState ?: return
|
||||
val tapWalletManager = globalState.tapWalletManager
|
||||
|
||||
when (action) {
|
||||
is WalletAction.MultiWallet.AddWalletManagers -> {
|
||||
store.state.globalState.feedbackManager?.infoHolder
|
||||
?.setWalletsInfo(action.walletManagers)
|
||||
globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers)
|
||||
}
|
||||
is WalletAction.MultiWallet.SelectWallet -> {
|
||||
if (action.walletData != null) {
|
||||
|
|
@ -35,7 +37,7 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.AddToken -> {
|
||||
globalState?.scanResponse?.card?.cardId?.let {
|
||||
globalState.scanResponse?.card?.cardId?.let {
|
||||
currenciesRepository.saveAddedToken(it, action.token)
|
||||
}
|
||||
addToken(action.token, walletState, globalState)
|
||||
|
|
@ -44,35 +46,33 @@ class MultiWalletMiddleware {
|
|||
addTokens(action.tokens, walletState, globalState)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
globalState?.scanResponse?.card?.let { card ->
|
||||
currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain)
|
||||
globalState.scanResponse?.let {
|
||||
currenciesRepository.saveAddedBlockchain(it.card.cardId, action.blockchain)
|
||||
if (walletState?.blockchains?.contains(action.blockchain) != true) {
|
||||
globalState.tapWalletManager.walletManagerFactory
|
||||
.makeWalletManagerForApp(card, action.blockchain)?.let {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it))
|
||||
tapWalletManager.walletManagerFactory
|
||||
.makeWalletManagerForApp(it, action.blockchain)?.let { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
}
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Blockchain(action.blockchain)))
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
moonpayStatus = globalState?.moonpayStatus,
|
||||
moonpayStatus = globalState.moonpayStatus,
|
||||
blockchain = action.blockchain
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
is WalletAction.MultiWallet.SaveCurrencies -> {
|
||||
val cardId = globalState?.scanResponse?.card?.cardId
|
||||
cardId?.let { currenciesRepository.saveCardCurrencies(it, action.cardCurrencies) }
|
||||
globalState.scanResponse?.card?.cardId?.let {
|
||||
currenciesRepository.saveCardCurrencies(it, action.cardCurrencies)
|
||||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
val cardId = globalState?.scanResponse?.card?.cardId
|
||||
val cardId = globalState.scanResponse?.card?.cardId
|
||||
when (val currency = action.walletData.currency) {
|
||||
is Currency.Blockchain -> {
|
||||
cardId?.let {
|
||||
currenciesRepository.removeBlockchain(
|
||||
it,
|
||||
currency.blockchain
|
||||
)
|
||||
currenciesRepository.removeBlockchain(it, currency.blockchain)
|
||||
}
|
||||
}
|
||||
is Currency.Token -> {
|
||||
|
|
@ -83,11 +83,13 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.FindBlockchainsInUse -> {
|
||||
val cardFirmware = globalState?.scanResponse?.card?.firmwareVersion
|
||||
val scanResponse = globalState.scanResponse ?: return
|
||||
|
||||
val cardFirmware = scanResponse.card.firmwareVersion
|
||||
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
|
||||
.filterNot { walletState?.blockchains?.contains(it) == true }
|
||||
val walletManagers =
|
||||
action.factory.makeWalletManagersForApp(action.card, blockchains)
|
||||
tapWalletManager.walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
|
||||
|
||||
scope.launch {
|
||||
walletManagers.map { walletManager ->
|
||||
|
|
@ -120,12 +122,13 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.FindTokensInUse -> {
|
||||
val card = globalState?.scanResponse?.card ?: return
|
||||
val scanResponse = globalState.scanResponse ?: return
|
||||
|
||||
val walletFactory = tapWalletManager.walletManagerFactory
|
||||
val card = scanResponse.card
|
||||
val walletManager = walletState?.getWalletManager(Blockchain.Ethereum)
|
||||
?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
|
||||
card = card,
|
||||
blockchain = Blockchain.Ethereum
|
||||
)
|
||||
?: walletFactory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
|
||||
val tokenFinder = walletManager as TokenFinder
|
||||
scope.launch {
|
||||
val result = tokenFinder.findTokens()
|
||||
|
|
@ -159,19 +162,18 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
|
||||
private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) {
|
||||
val card = globalState?.scanResponse?.card ?: return
|
||||
val scanResponse = globalState?.scanResponse ?: return
|
||||
|
||||
val walletManager = walletState?.getWalletManager(token)
|
||||
?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
|
||||
card = card,
|
||||
blockchain = token.blockchain
|
||||
scanResponse,
|
||||
token.blockchain
|
||||
)?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
}
|
||||
|
||||
store.dispatch(
|
||||
WalletAction.LoadFiatRate(currency = Currency.Token(token))
|
||||
)
|
||||
store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Token(token)))
|
||||
|
||||
scope.launch {
|
||||
when (val result = walletManager?.addToken(token)) {
|
||||
|
|
@ -189,12 +191,13 @@ class MultiWalletMiddleware {
|
|||
walletState: WalletState?,
|
||||
globalState: GlobalState?,
|
||||
) {
|
||||
val card = globalState?.scanResponse?.card ?: return
|
||||
val scanResponse = globalState?.scanResponse ?: return
|
||||
|
||||
val tokensWithManagers = tokens.map { token ->
|
||||
val walletManager = walletState?.getWalletManager(token)
|
||||
?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
|
||||
card = card,
|
||||
blockchain = token.blockchain
|
||||
scanResponse,
|
||||
token.blockchain
|
||||
)?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.squareup.moshi.FromJson
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.ToJson
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
|
|
@ -41,7 +42,10 @@ fun createMoshiConverterFactory(): Converter.Factory = MoshiConverterFactory.cre
|
|||
|
||||
fun createMoshi(): Moshi = Moshi.Builder()
|
||||
.add(BigDecimalAdapter)
|
||||
.add(KotlinJsonAdapterFactory()).build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(TangemSdkAdapter.DerivationPathAdapter())
|
||||
.add(TangemSdkAdapter.DerivationNodeAdapter())
|
||||
.build()
|
||||
|
||||
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
val logging = HttpLoggingInterceptor()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue