Updated on 2026-08-14
This commit is contained in:
parent
96868cdf9d
commit
05113dc124
56 changed files with 624 additions and 516 deletions
|
|
@ -83,6 +83,8 @@ dependencies {
|
|||
// WebView
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
|
||||
//lifecycle
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
"isWalletPayIdEnabled": true,
|
||||
"isSendingToPayIdEnabled": true,
|
||||
"isTopUpEnabled": true,
|
||||
"isCreatingTwinCardsAllowed": false
|
||||
"isCreatingTwinCardsAllowed": true
|
||||
}
|
||||
|
|
@ -7,10 +7,7 @@ import android.util.Log
|
|||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.CardFilter
|
||||
import com.tangem.Config
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.commands.common.card.CardType
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.tap.common.DialogManager
|
||||
import com.tangem.tap.common.IntentHandler
|
||||
|
|
@ -30,7 +27,6 @@ import kotlinx.coroutines.Job
|
|||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
lateinit var tangemSdk: TangemSdk
|
||||
|
|
@ -68,10 +64,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
|
|||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
|
||||
|
||||
tangemSdk = TangemSdk.init(
|
||||
this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java)))
|
||||
)
|
||||
tangemSdkManager = TangemSdkManager(this)
|
||||
tangemSdk = TangemSdk.init(this, TangemSdkManager.config)
|
||||
tangemSdkManager = TangemSdkManager(tangemSdk, this)
|
||||
|
||||
store.dispatch(WalletConnectAction.RestoreSessions)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
||||
|
||||
interface AnalyticsHandler {
|
||||
fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: Blockchain? = null)
|
||||
fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
|
||||
fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: FirebaseAnalyticsHandler.ActionToLog,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ import androidx.core.os.bundleOf
|
|||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
|
||||
object FirebaseAnalyticsHandler : AnalyticsHandler {
|
||||
override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: Blockchain?) {
|
||||
override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: String?) {
|
||||
Firebase.analytics.logEvent(event.event, setData(card, blockchain))
|
||||
}
|
||||
|
||||
|
|
@ -47,18 +46,17 @@ object FirebaseAnalyticsHandler : AnalyticsHandler {
|
|||
|
||||
private fun getParamsFromCard(card: Card): Map<AnalyticsParam, String> {
|
||||
return mapOf(
|
||||
AnalyticsParam.FIRMWARE to card.firmwareVersion.version,
|
||||
AnalyticsParam.BATCH_ID to card.cardData?.batchId
|
||||
AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue,
|
||||
AnalyticsParam.BATCH_ID to card.batchId
|
||||
).filterNotNull()
|
||||
}
|
||||
|
||||
private fun setData(card: Card?, blockchain: Blockchain?): Bundle {
|
||||
private fun setData(card: Card?, blockchain: String?): Bundle {
|
||||
if (card == null) return bundleOf()
|
||||
return bundleOf(
|
||||
AnalyticsParam.BLOCKCHAIN.param to (blockchain?.currency
|
||||
?: card.cardData?.blockchainName),
|
||||
AnalyticsParam.BATCH_ID.param to card.cardData?.batchId,
|
||||
AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version
|
||||
AnalyticsParam.BLOCKCHAIN.param to (blockchain),
|
||||
AnalyticsParam.BATCH_ID.param to card.batchId,
|
||||
AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
||||
object TangemSdk {
|
||||
|
||||
|
|
@ -31,17 +31,15 @@ object TangemSdk {
|
|||
is TangemSdkError.WalletNotFound -> TangemSdkError.WalletNotFound()
|
||||
is TangemSdkError.AlreadyPersonalized -> TangemSdkError.AlreadyPersonalized()
|
||||
is TangemSdkError.CannotBeDepersonalized -> TangemSdkError.CannotBeDepersonalized()
|
||||
is TangemSdkError.Pin1Required -> TangemSdkError.Pin1Required()
|
||||
is TangemSdkError.AccessCodeRequired -> TangemSdkError.AccessCodeRequired()
|
||||
is TangemSdkError.CardReadWrongWallet -> TangemSdkError.CardReadWrongWallet()
|
||||
is TangemSdkError.CardWithMaxZeroWallets -> TangemSdkError.CardWithMaxZeroWallets()
|
||||
is TangemSdkError.AlreadyCreated -> TangemSdkError.AlreadyCreated()
|
||||
is TangemSdkError.WalletIndexExceedsMaxValue -> TangemSdkError.WalletIndexExceedsMaxValue()
|
||||
is TangemSdkError.MaxNumberOfWalletsCreated -> TangemSdkError.MaxNumberOfWalletsCreated()
|
||||
is TangemSdkError.WalletIndexNotCorrect -> TangemSdkError.WalletIndexNotCorrect()
|
||||
is TangemSdkError.PurgeWalletProhibited -> TangemSdkError.PurgeWalletProhibited()
|
||||
is TangemSdkError.Pin1CannotBeChanged -> TangemSdkError.Pin1CannotBeChanged()
|
||||
is TangemSdkError.Pin2CannotBeChanged -> TangemSdkError.Pin2CannotBeChanged()
|
||||
is TangemSdkError.Pin1CannotBeDefault -> TangemSdkError.Pin1CannotBeDefault()
|
||||
is TangemSdkError.AccessCodeCannotBeChanged -> TangemSdkError.AccessCodeCannotBeChanged()
|
||||
is TangemSdkError.PasscodeCannotBeChanged -> TangemSdkError.PasscodeCannotBeChanged()
|
||||
is TangemSdkError.AccessCodeCannotBeDefault -> TangemSdkError.AccessCodeCannotBeDefault()
|
||||
is TangemSdkError.NoRemainingSignatures -> TangemSdkError.NoRemainingSignatures()
|
||||
is TangemSdkError.EmptyHashes -> TangemSdkError.EmptyHashes()
|
||||
is TangemSdkError.HashSizeMustBeEqual -> TangemSdkError.HashSizeMustBeEqual()
|
||||
|
|
@ -52,7 +50,7 @@ object TangemSdk {
|
|||
is TangemSdkError.NotPersonalized -> TangemSdkError.NotPersonalized()
|
||||
is TangemSdkError.NotActivated -> TangemSdkError.NotActivated()
|
||||
is TangemSdkError.WalletIsPurged -> TangemSdkError.WalletIsPurged()
|
||||
is TangemSdkError.Pin2OrCvcRequired -> TangemSdkError.Pin2OrCvcRequired()
|
||||
is TangemSdkError.PasscodeRequired -> TangemSdkError.PasscodeRequired()
|
||||
is TangemSdkError.VerificationFailed -> TangemSdkError.VerificationFailed()
|
||||
is TangemSdkError.DataSizeTooLarge -> TangemSdkError.DataSizeTooLarge()
|
||||
is TangemSdkError.MissingCounter -> TangemSdkError.MissingCounter()
|
||||
|
|
@ -60,8 +58,8 @@ object TangemSdk {
|
|||
is TangemSdkError.DataCannotBeWritten -> TangemSdkError.DataCannotBeWritten()
|
||||
is TangemSdkError.MissingIssuerPubicKey -> TangemSdkError.MissingIssuerPubicKey()
|
||||
is TangemSdkError.CardVerificationFailed -> TangemSdkError.CardVerificationFailed()
|
||||
is TangemSdkError.WrongPin1 -> TangemSdkError.WrongPin1()
|
||||
is TangemSdkError.WrongPin2 -> TangemSdkError.WrongPin2()
|
||||
is TangemSdkError.WrongAccessCode -> TangemSdkError.WrongAccessCode()
|
||||
is TangemSdkError.WrongPasscode -> TangemSdkError.WrongPasscode()
|
||||
is TangemSdkError.UnknownError -> TangemSdkError.UnknownError()
|
||||
is TangemSdkError.UserCancelled -> TangemSdkError.UserCancelled()
|
||||
is TangemSdkError.Busy -> TangemSdkError.Busy()
|
||||
|
|
@ -69,8 +67,14 @@ object TangemSdk {
|
|||
is TangemSdkError.WrongCardNumber -> TangemSdkError.WrongCardNumber()
|
||||
is TangemSdkError.WrongCardType -> TangemSdkError.WrongCardType()
|
||||
is TangemSdkError.CardError -> TangemSdkError.CardError()
|
||||
is TangemSdkError.FirmwareNotSupported -> TangemSdkError.FirmwareNotSupported()
|
||||
is TangemSdkError.NotSupportedFirmwareVersion -> TangemSdkError.NotSupportedFirmwareVersion()
|
||||
is TangemSdkError.WalletError -> TangemSdkError.WalletError()
|
||||
is TangemSdkError.WalletCannotBeCreated -> TangemSdkError.WalletCannotBeCreated()
|
||||
is TangemSdkError.UnsupportedCurve -> TangemSdkError.UnsupportedCurve()
|
||||
is TangemSdkError.UnsupportedWalletConfig -> TangemSdkError.UnsupportedWalletConfig()
|
||||
is TangemSdkError.CryptoUtilsError -> TangemSdkError.CryptoUtilsError()
|
||||
is TangemSdkError.NetworkError -> TangemSdkError.NetworkError(error.customMessage)
|
||||
is TangemSdkError.ExceptionError -> TangemSdkError.ExceptionError(error.throwable)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ sealed class GlobalAction : Action {
|
|||
}
|
||||
|
||||
data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction()
|
||||
|
||||
data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction()
|
||||
|
||||
data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction()
|
||||
object RestoreAppCurrency : GlobalAction() {
|
||||
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.commands.wallet.WalletIndex
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import org.rekotlin.Action
|
||||
|
||||
fun globalReducer(action: Action, state: AppState): GlobalState {
|
||||
|
|
@ -30,29 +28,11 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
globalState.copy(configManager = action.configManager)
|
||||
}
|
||||
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
|
||||
is GlobalAction.UpdateSecurityOptions -> {
|
||||
val card = when (action.securityOption) {
|
||||
SecurityOption.LongTap -> globalState.scanNoteResponse?.card?.copy(
|
||||
isPin1Default = true, isPin2Default = true
|
||||
)
|
||||
SecurityOption.PassCode -> globalState.scanNoteResponse?.card?.copy(
|
||||
isPin1Default = true, isPin2Default = false
|
||||
)
|
||||
SecurityOption.AccessCode -> globalState.scanNoteResponse?.card?.copy(
|
||||
isPin1Default = false, isPin2Default = true
|
||||
)
|
||||
}
|
||||
if (card != null) {
|
||||
globalState.copy(scanNoteResponse = globalState.scanNoteResponse?.copy(card = card))
|
||||
} else {
|
||||
globalState
|
||||
}
|
||||
}
|
||||
is GlobalAction.UpdateWalletSignedHashes -> {
|
||||
val wallet = globalState.scanNoteResponse?.card
|
||||
?.wallet(WalletIndex.PublicKey(action.walletPublicKey))
|
||||
?.wallet(action.walletPublicKey)
|
||||
?.copy(
|
||||
signedHashes = action.walletSignedHashes,
|
||||
totalSignedHashes = action.walletSignedHashes,
|
||||
remainingSignatures = action.remainingSignatures
|
||||
)
|
||||
val card = globalState.scanNoteResponse?.card
|
||||
|
|
@ -76,6 +56,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
is GlobalAction.GetMoonPayUserStatus.Success -> {
|
||||
globalState.copy(moonPayUserStatus = action.moonPayUserStatus)
|
||||
}
|
||||
is GlobalAction.SetIfCardVerifiedOnline ->
|
||||
globalState.copy(cardVerifiedOnline = action.verified)
|
||||
|
||||
else -> globalState
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.commands.common.network.TangemService
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
|
|
@ -14,10 +13,10 @@ import org.rekotlin.StateType
|
|||
|
||||
data class GlobalState(
|
||||
val scanNoteResponse: ScanNoteResponse? = null,
|
||||
val cardVerifiedOnline: Boolean = false,
|
||||
val tapWalletManager: TapWalletManager = TapWalletManager(),
|
||||
val payIdManager: PayIdManager = PayIdManager(),
|
||||
val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(),
|
||||
val tangemService: TangemService = TangemService(),
|
||||
val configManager: ConfigManager? = null,
|
||||
val warningManager: WarningMessagesManager? = null,
|
||||
val feedbackManager: FeedbackManager? = null,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.network.payid.PayIdVerifyService
|
||||
import com.tangem.tap.network.payid.VerifyPayIdResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.tangem.*
|
||||
import com.tangem.commands.CommandResponse
|
||||
import com.tangem.commands.PinType
|
||||
import com.tangem.commands.SetPinCommand
|
||||
import com.tangem.commands.SetPinResponse
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardType
|
||||
import com.tangem.commands.wallet.PurgeWalletCommand
|
||||
import com.tangem.commands.wallet.PurgeWalletResponse
|
||||
import android.content.Context
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.common.CardFilter
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.Card
|
||||
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.operations.CommandResponse
|
||||
import com.tangem.operations.pins.CheckUserCodesCommand
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.wallet.PurgeWalletCommand
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.domain.extensions.getDefaultWalletIndex
|
||||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
import com.tangem.tap.domain.tasks.ScanNoteTask
|
||||
|
|
@ -25,23 +26,19 @@ import com.tangem.tap.domain.twins.isTwinCard
|
|||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class TangemSdkManager(val activity: ComponentActivity) {
|
||||
private val tangemSdk = TangemSdk.init(
|
||||
activity, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java)))
|
||||
)
|
||||
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
|
||||
|
||||
suspend fun scanNote(
|
||||
analyticsHandler: AnalyticsHandler, messageRes: Int? = null
|
||||
analyticsHandler: AnalyticsHandler, messageRes: Int? = null,
|
||||
): CompletionResult<ScanNoteResponse> {
|
||||
analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
|
||||
val result = runTaskAsyncReturnOnMain(ScanNoteTask(),
|
||||
initialMessage = Message(
|
||||
activity.getString(messageRes ?: R.string.initial_message_scan_header)
|
||||
))
|
||||
val result = runTaskAsyncReturnOnMain(ScanNoteTask(),
|
||||
initialMessage = Message(
|
||||
context.getString(messageRes ?: R.string.initial_message_scan_header)
|
||||
))
|
||||
if (result is CompletionResult.Failure) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan)
|
||||
|
|
@ -52,53 +49,57 @@ class TangemSdkManager(val activity: ComponentActivity) {
|
|||
|
||||
suspend fun createWallet(cardId: String?): CompletionResult<Card> {
|
||||
return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId,
|
||||
initialMessage = Message(activity.getString(R.string.initial_message_create_wallet_body)))
|
||||
initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)))
|
||||
}
|
||||
|
||||
suspend fun eraseWallet(cardId: String?): CompletionResult<PurgeWalletResponse> {
|
||||
return runTaskAsyncReturnOnMain(PurgeWalletCommand(
|
||||
TangemSdkConstants.getDefaultWalletIndex()),
|
||||
cardId,
|
||||
initialMessage = Message(activity.getString(R.string.initial_message_purge_wallet_body)))
|
||||
suspend fun eraseWallet(card: Card): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
PurgeWalletCommand(card.wallets.first().publicKey),
|
||||
card.cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_purge_wallet_body)))
|
||||
}
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand(
|
||||
pinType = PinType.Pin2,
|
||||
newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
|
||||
newPin2 = null
|
||||
), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_passcode_body)))
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_change_passcode_body)))
|
||||
}
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand(
|
||||
pinType = PinType.Pin1,
|
||||
newPin1 = null,
|
||||
newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
|
||||
), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_access_code_body)))
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changeAccessCode(null),
|
||||
cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_change_access_code_body)))
|
||||
}
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SetPinResponse> {
|
||||
return runTaskAsyncReturnOnMain(SetPinCommand(
|
||||
pinType = PinType.Pin1,
|
||||
newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
|
||||
newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
|
||||
), cardId, initialMessage = Message(activity.getString(R.string.initial_message_tap_header)))
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.resetUserCodes(),
|
||||
cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
|
||||
}
|
||||
|
||||
suspend fun checkUserCodes(cardId: String?): CompletionResult<CheckUserCodesResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
CheckUserCodesCommand(),
|
||||
cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
|
||||
}
|
||||
|
||||
suspend fun <T : CommandResponse> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
): CompletionResult<T> =
|
||||
withContext(Dispatchers.IO) {
|
||||
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,
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
): CompletionResult<T> {
|
||||
val result = runTaskAsync(runnable, cardId, initialMessage)
|
||||
return withContext(Dispatchers.Main) { result }
|
||||
|
|
@ -107,4 +108,12 @@ class TangemSdkManager(val activity: ComponentActivity) {
|
|||
fun changeDisplayedCardIdNumbersCount(card: Card) {
|
||||
tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTwinCard()) 4 else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
val config = Config(
|
||||
filter = CardFilter(
|
||||
allowedCardTypes = FirmwareVersion.FirmwareType.values().toList()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,26 +3,25 @@ package com.tangem.tap.domain
|
|||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.blockchain.common.TransactionSigner
|
||||
import com.tangem.commands.SignCommand
|
||||
import com.tangem.commands.SignResponse
|
||||
import com.tangem.commands.wallet.WalletIndex
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tap.domain.tasks.SignHashTask
|
||||
import com.tangem.tap.domain.tasks.SignHashesTask
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class TangemSigner(
|
||||
private val tangemSdk: TangemSdk,
|
||||
private val initialMessage: Message,
|
||||
private val signerCallback: (SignResponse) -> Unit
|
||||
private val signerCallback: (TangemSignerResponse) -> Unit,
|
||||
) : TransactionSigner {
|
||||
|
||||
override suspend fun sign(
|
||||
hash: ByteArray,
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray
|
||||
walletPublicKey: ByteArray,
|
||||
): CompletionResult<ByteArray> =
|
||||
suspendCoroutine { continuation ->
|
||||
val command = SignCommand(arrayOf(hash), WalletIndex.PublicKey(walletPublicKey))
|
||||
val command = SignHashTask(hash, walletPublicKey)
|
||||
tangemSdk.startSessionWithRunnable(
|
||||
runnable = command,
|
||||
cardId = cardId,
|
||||
|
|
@ -30,8 +29,13 @@ class TangemSigner(
|
|||
) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
signerCallback(result.data)
|
||||
continuation.resume(CompletionResult.Success(result.data.signatures.first()))
|
||||
signerCallback(
|
||||
TangemSignerResponse(
|
||||
result.data.totalSignedHashes,
|
||||
result.data.remainingSignatures
|
||||
)
|
||||
)
|
||||
continuation.resume(CompletionResult.Success(result.data.signature))
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
continuation.resume(CompletionResult.Failure(result.error))
|
||||
|
|
@ -43,18 +47,23 @@ class TangemSigner(
|
|||
override suspend fun sign(
|
||||
hashes: List<ByteArray>,
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray
|
||||
walletPublicKey: ByteArray,
|
||||
): CompletionResult<List<ByteArray>> =
|
||||
suspendCoroutine { continuation ->
|
||||
val command = SignCommand(hashes.toTypedArray(), WalletIndex.PublicKey(walletPublicKey))
|
||||
val task = SignHashesTask(hashes, walletPublicKey)
|
||||
tangemSdk.startSessionWithRunnable(
|
||||
runnable = command,
|
||||
runnable = task,
|
||||
cardId = cardId,
|
||||
initialMessage = initialMessage,
|
||||
) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
signerCallback(result.data)
|
||||
signerCallback(
|
||||
TangemSignerResponse(
|
||||
result.data.totalSignedHashes,
|
||||
result.data.remainingSignatures
|
||||
)
|
||||
)
|
||||
continuation.resume(CompletionResult.Success(result.data.signatures))
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
|
|
@ -62,4 +71,9 @@ class TangemSigner(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TangemSignerResponse(
|
||||
val totalSignedHashes: Int?,
|
||||
val remainingSignatures: Int?,
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.TangemError
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.wallet.R
|
||||
|
||||
interface TapErrors
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardStatus
|
||||
import com.tangem.commands.common.network.Result
|
||||
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
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
|
|
@ -89,7 +88,7 @@ class TapWalletManager {
|
|||
|
||||
suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) {
|
||||
if (addAnalyticsEvent) {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card)
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain)
|
||||
}
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
|
||||
updateConfigManager(data)
|
||||
|
|
@ -109,7 +108,7 @@ class TapWalletManager {
|
|||
}
|
||||
}
|
||||
|
||||
val blockchain = data.card.getBlockchain()
|
||||
val blockchain = data.getBlockchain()
|
||||
if (blockchain == Blockchain.Ethereum ||
|
||||
blockchain == Blockchain.EthereumTestnet) {
|
||||
store.dispatch(TokensAction.LoadCardTokens)
|
||||
|
|
@ -120,12 +119,12 @@ class TapWalletManager {
|
|||
|
||||
private fun updateConfigManager(data: ScanNoteResponse) {
|
||||
val configManager = store.state.globalState.configManager
|
||||
val blockchain = data.card.getBlockchain()
|
||||
val blockchain = data.getBlockchain()
|
||||
if (data.card.isStart2Coin) {
|
||||
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
|
||||
configManager?.turnOff(ConfigManager.isTopUpEnabled)
|
||||
} else if (blockchain == Blockchain.Bitcoin
|
||||
|| data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) {
|
||||
|| data.walletData?.blockchain == Blockchain.Bitcoin.id) {
|
||||
configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
|
||||
configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
|
||||
} else {
|
||||
|
|
@ -136,25 +135,25 @@ class TapWalletManager {
|
|||
|
||||
suspend fun loadData(data: ScanNoteResponse) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val artworkId = data.verifyResponse?.artworkInfo?.id
|
||||
|
||||
store.dispatch(WalletAction.LoadCardInfo(data.card))
|
||||
|
||||
when {
|
||||
data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
|
||||
store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
|
||||
}
|
||||
data.card.getStatus() == CardStatus.Empty ||
|
||||
data.card.wallets.isEmpty() ||
|
||||
(data.card.isTwinCard() && data.secondTwinPublicKey == null) -> {
|
||||
store.dispatch(WalletAction.EmptyWallet)
|
||||
store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
|
||||
}
|
||||
else -> {
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
|
||||
val blockchain = data.card.getBlockchain()
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != null && primaryWalletManager != null) {
|
||||
val primaryToken = data.card.getToken()
|
||||
val primaryToken = data.getPrimaryToken()
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
|
||||
if (primaryToken != null) {
|
||||
|
|
@ -173,17 +172,14 @@ class TapWalletManager {
|
|||
loadMultiWalletData(data.card, blockchain, null)
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id))
|
||||
val moonPayUserStatus = store.state.globalState.moonPayUserStatus
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true,
|
||||
allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true,
|
||||
))
|
||||
store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.Warnings.CheckIfNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -236,10 +232,10 @@ class TapWalletManager {
|
|||
suspend fun reloadData(data: ScanNoteResponse) {
|
||||
withContext(Dispatchers.Main) {
|
||||
when {
|
||||
data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
|
||||
}
|
||||
data.card.getStatus() == CardStatus.Empty ||
|
||||
data.card.wallets.isEmpty() ||
|
||||
(data.card.isTwinCard() && data.secondTwinPublicKey == null) -> {
|
||||
store.dispatch(WalletAction.EmptyWallet)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.EllipticCurve
|
||||
import com.tangem.commands.common.card.masks.Product
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.tap.domain.TapWorkarounds.isNote
|
||||
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.twins.isTwinCard
|
||||
import java.util.*
|
||||
|
||||
object TapWorkarounds {
|
||||
|
|
@ -14,21 +17,37 @@ object TapWorkarounds {
|
|||
}
|
||||
|
||||
val Card.isStart2Coin: Boolean
|
||||
get() = isStart2CoinIssuer(cardData?.issuerName)
|
||||
|
||||
fun Card.isExcluded(): Boolean {
|
||||
val cardData = this.cardData ?: return false
|
||||
val productMask = cardData.productMask
|
||||
val excludedBatch = excludedBatches.contains(cardData.batchId)
|
||||
val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.uppercase(Locale.ROOT))
|
||||
val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later
|
||||
!productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard))
|
||||
return excludedBatch || excludedIssuerName || excludedProductMask
|
||||
|
||||
}
|
||||
get() = isStart2CoinIssuer(issuer.name)
|
||||
|
||||
val Card.isTestCard: Boolean
|
||||
get() = cardData?.batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
|
||||
get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
|
||||
|
||||
fun Card.isExcluded(): Boolean {
|
||||
val excludedBatch = excludedBatches.contains(batchId)
|
||||
val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
|
||||
return excludedBatch || excludedIssuerName
|
||||
}
|
||||
|
||||
fun Card.isNote(): Boolean {
|
||||
return notesBatches.contains(batchId)
|
||||
}
|
||||
|
||||
fun Card.isMultiCurrencyWallet(): Boolean {
|
||||
return multiCurrencyWalletsBatches.contains(batchId)
|
||||
}
|
||||
|
||||
val Card.noteCurrency: Blockchain?
|
||||
get() {
|
||||
return when (batchId) {
|
||||
"AB01" -> Blockchain.Bitcoin
|
||||
"AB02" -> Blockchain.Ethereum
|
||||
"AB03" -> Blockchain.CardanoShelley
|
||||
"AB04" -> Blockchain.Dogecoin
|
||||
"AB05" -> Blockchain.Binance
|
||||
"AB06" -> Blockchain.XRP
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private const val START_2_COIN_ISSUER = "start2coin"
|
||||
private const val TEST_CARD_BATCH = "99FF"
|
||||
|
|
@ -44,12 +63,22 @@ object TapWorkarounds {
|
|||
private val excludedIssuers = listOf(
|
||||
"TTM BANK"
|
||||
)
|
||||
|
||||
private val notesBatches = listOf(
|
||||
"AB01",
|
||||
"AB02",
|
||||
"AB03",
|
||||
"AB04",
|
||||
"AB05",
|
||||
"AB06",
|
||||
)
|
||||
|
||||
private val multiCurrencyWalletsBatches = listOf("AC01")
|
||||
}
|
||||
|
||||
val Card.isMultiwalletAllowed: Boolean
|
||||
get() {
|
||||
return cardData?.productMask?.contains(Product.TwinCard) != true
|
||||
&& !isStart2Coin
|
||||
&& (firmwareVersion.major >= 4 ||
|
||||
return !isTwinCard() && !isStart2Coin && !isNote()
|
||||
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
|
||||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.configurable.config
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.domain.configurable.Loader
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.configurable.warningMessage
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.extensions.containsAny
|
||||
import com.tangem.tap.common.extensions.removeBy
|
||||
import com.tangem.wallet.R
|
||||
|
|
|
|||
|
|
@ -1,48 +1,18 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.FirmwareConstraints
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardStatus
|
||||
import com.tangem.commands.wallet.CardWallet
|
||||
import com.tangem.commands.wallet.WalletStatus
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
|
||||
fun Card.getToken(): Token? {
|
||||
val symbol = cardData?.tokenSymbol ?: return null
|
||||
val contractAddress = cardData?.tokenContractAddress ?: return null
|
||||
val decimals = cardData?.tokenDecimal ?: return null
|
||||
val blockchain = cardData?.blockchainName?.let { Blockchain.fromId(it) } ?: return null
|
||||
if (symbol.isBlank() || contractAddress.isBlank()) return null
|
||||
return Token(symbol, contractAddress, decimals, blockchain)
|
||||
}
|
||||
|
||||
fun Card.getBlockchain(): Blockchain? {
|
||||
val blockchainName: String = cardData?.blockchainName ?: return null
|
||||
return Blockchain.fromId(blockchainName)
|
||||
}
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
|
||||
fun Card.getSingleWallet(): CardWallet? {
|
||||
return wallet(TangemSdkConstants.getDefaultWalletIndex())
|
||||
}
|
||||
|
||||
fun Card.getStatus(): CardStatus {
|
||||
if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!!
|
||||
|
||||
return if (wallets.any { it.status == WalletStatus.Loaded }) {
|
||||
CardStatus.Loaded
|
||||
} else {
|
||||
CardStatus.Empty
|
||||
}
|
||||
return wallets.firstOrNull()
|
||||
}
|
||||
|
||||
fun Card.hasSignedHashes(): Boolean {
|
||||
return wallets.any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 }
|
||||
return wallets.any { it.totalSignedHashes ?: 0 > 0 }
|
||||
}
|
||||
|
||||
fun Card.signedHashesCount(): Int {
|
||||
return wallets.map { it.signedHashes ?: 0 }.sum()
|
||||
return wallets.map { it.totalSignedHashes ?: 0 }.sum()
|
||||
}
|
||||
|
||||
val Card.remainingSignatures: Int?
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.commands.wallet.WalletIndex
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
|
||||
fun TangemSdkConstants.Companion.getDefaultWalletIndex(): WalletIndex {
|
||||
return WalletIndex.Index(oldCardDefaultWalletIndex)
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@ 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.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.EllipticCurve
|
||||
import com.tangem.commands.wallet.CardWallet
|
||||
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.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
|
|
@ -54,9 +54,9 @@ fun WalletManagerFactory.makePrimaryWalletManager(
|
|||
): WalletManager? {
|
||||
val card = data.card
|
||||
val blockchain = if (card.isTestCard) {
|
||||
card.getBlockchain()?.getTestnetVersion()
|
||||
data.getBlockchain()?.getTestnetVersion()
|
||||
} else {
|
||||
card.getBlockchain()
|
||||
data.getBlockchain()
|
||||
}
|
||||
val supportedCurves = blockchain?.getSupportedCurves() ?: return null
|
||||
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
|
||||
|
|
|
|||
|
|
@ -1,28 +1,27 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.FirmwareConstraints
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tasks.CreateWalletTask
|
||||
import com.tangem.tasks.PreflightReadCapable
|
||||
import com.tangem.tasks.PreflightReadSettings
|
||||
import com.tangem.tasks.PreflightReadTask
|
||||
import com.tangem.common.card.Card
|
||||
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.wallet.CreateWalletCommand
|
||||
|
||||
class CreateWalletAndRescanTask : CardSessionRunnable<Card>, PreflightReadCapable {
|
||||
override val requiresPin2 = false
|
||||
override fun preflightReadSettings() = PreflightReadSettings.FullCardRead
|
||||
class CreateWalletAndRescanTask : CardSessionRunnable<Card> {
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
val firmwareVerion = session.environment.card?.firmwareVersion
|
||||
if (firmwareVerion == null) {
|
||||
val card = session.environment.card.guard {
|
||||
callback(CompletionResult.Failure(TangemSdkError.CardError()))
|
||||
return
|
||||
}
|
||||
val task = if (firmwareVerion < FirmwareConstraints.AvailabilityVersions.walletData) {
|
||||
CreateWalletTask()
|
||||
val firmwareVersion = card.firmwareVersion
|
||||
|
||||
val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
|
||||
CreateWalletCommand(card.supportedCurves.first(), false)
|
||||
} else {
|
||||
CreateWalletsTask()
|
||||
}
|
||||
|
|
@ -30,7 +29,7 @@ class CreateWalletAndRescanTask : CardSessionRunnable<Card>, PreflightReadCapabl
|
|||
task.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback)
|
||||
PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback)
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,47 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.EllipticCurve
|
||||
import com.tangem.commands.wallet.CreateWalletCommand
|
||||
import com.tangem.commands.wallet.WalletConfig
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tasks.PreflightReadSettings
|
||||
import com.tangem.tasks.PreflightReadTask
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.wallet.CreateWalletCommand
|
||||
|
||||
class CreateWalletsTask(wallets: List<WalletConfig> = emptyList()) : CardSessionRunnable<Card> {
|
||||
override val requiresPin2 = false
|
||||
class CreateWalletsTask(curves: List<EllipticCurve> = emptyList()) : CardSessionRunnable<Card> {
|
||||
|
||||
val wallets = if (wallets.isEmpty()) {
|
||||
private val curves = if (curves.isEmpty()) {
|
||||
listOf(
|
||||
WalletConfig(null, null, EllipticCurve.Secp256k1, null),
|
||||
WalletConfig(null, null, EllipticCurve.Ed25519, null),
|
||||
WalletConfig(null, null, EllipticCurve.Secp256r1, null),
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Secp256r1,
|
||||
)
|
||||
} else {
|
||||
wallets
|
||||
curves
|
||||
}
|
||||
|
||||
var index = 0
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
val walletConfig = wallets[index]
|
||||
createWallet(walletConfig, session, callback)
|
||||
val curve = curves[index]
|
||||
createWallet(curve, session, callback)
|
||||
}
|
||||
|
||||
private fun createWallet(
|
||||
walletConfig: WalletConfig, session: CardSession,
|
||||
curve: EllipticCurve, session: CardSession,
|
||||
callback: (result: CompletionResult<Card>) -> Unit
|
||||
) {
|
||||
|
||||
CreateWalletCommand(
|
||||
walletConfig, walletIndexValue = walletConfig.curveId!!.toWalletIndex()
|
||||
).run(session) { result ->
|
||||
CreateWalletCommand(curve, false).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
if (index == wallets.lastIndex) {
|
||||
PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback)
|
||||
if (index == curves.lastIndex) {
|
||||
PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback)
|
||||
return@run
|
||||
}
|
||||
index += 1
|
||||
createWallet(wallets[index], session, callback)
|
||||
createWallet(curves[index], session, callback)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
|
|
|
|||
|
|
@ -1,50 +1,67 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.*
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.commands.CommandResponse
|
||||
import com.tangem.commands.ReadIssuerDataCommand
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardStatus
|
||||
import com.tangem.commands.common.card.EllipticCurve
|
||||
import com.tangem.commands.verification.VerifyCardCommand
|
||||
import com.tangem.commands.verification.VerifyCardResponse
|
||||
import com.tangem.commands.wallet.WalletConfig
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.WalletData
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
||||
import com.tangem.tap.domain.TapWorkarounds.isMultiCurrencyWallet
|
||||
import com.tangem.tap.domain.TapWorkarounds.noteCurrency
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.extensions.getStatus
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.domain.twins.isTwinCard
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tasks.PreflightReadCapable
|
||||
import com.tangem.tasks.PreflightReadSettings
|
||||
import com.tangem.tasks.ScanTask
|
||||
import com.tangem.wallet.R
|
||||
|
||||
data class ScanNoteResponse(
|
||||
val card: Card,
|
||||
val verifyResponse: VerifyCardResponse? = null,
|
||||
val secondTwinPublicKey: String? = null,
|
||||
) : CommandResponse
|
||||
val card: Card,
|
||||
val walletData: WalletData?,
|
||||
val secondTwinPublicKey: String? = null,
|
||||
) : CommandResponse {
|
||||
|
||||
class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteResponse>, PreflightReadCapable {
|
||||
override val requiresPin2 = false
|
||||
fun getBlockchain(): Blockchain? {
|
||||
if (card.noteCurrency != null) return card.noteCurrency
|
||||
val blockchainName: String = walletData?.blockchain ?: return null
|
||||
return Blockchain.fromId(blockchainName)
|
||||
}
|
||||
|
||||
override fun preflightReadSettings() = PreflightReadSettings.FullCardRead
|
||||
fun getPrimaryToken(): Token? {
|
||||
val cardToken = walletData?.token ?: return null
|
||||
return Token(
|
||||
cardToken.name,
|
||||
cardToken.symbol,
|
||||
cardToken.contractAddress,
|
||||
cardToken.decimals,
|
||||
Blockchain.fromId(walletData.blockchain)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<ScanNoteResponse>) -> Unit) {
|
||||
class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteResponse> {
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanNoteResponse>) -> Unit,
|
||||
) {
|
||||
ScanTask().run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
|
||||
is CompletionResult.Success -> {
|
||||
val card = this.card?.copy(
|
||||
isPin1Default = result.data.isPin1Default,
|
||||
isPin2Default = result.data.isPin2Default,
|
||||
) ?: result.data
|
||||
val card = this.card ?: result.data
|
||||
|
||||
val error = getErrorIfExcludedCard(card)
|
||||
if (error != null) {
|
||||
|
|
@ -52,39 +69,28 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
|
|||
return@run
|
||||
}
|
||||
|
||||
verifyCard(card, session, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyCard(
|
||||
card: Card, session: CardSession, callback: (result: CompletionResult<ScanNoteResponse>) -> Unit
|
||||
) {
|
||||
VerifyCardCommand(true).run(session) { verifyResult ->
|
||||
when (verifyResult) {
|
||||
is CompletionResult.Success -> {
|
||||
if (card.isTwinCard()) {
|
||||
dealWithTwinCard(card, session, verifyResult.data, callback)
|
||||
dealWithTwinCard(card, session, callback)
|
||||
} else if (card.firmwareVersion.major >= 4) {
|
||||
createMissingWalletsIfNeeded(card, session, verifyResult.data, callback)
|
||||
createMissingWalletsIfNeeded(card, session, callback)
|
||||
} else {
|
||||
callback(CompletionResult.Success(ScanNoteResponse(card, verifyResult.data)))
|
||||
callback(CompletionResult.Success(
|
||||
ScanNoteResponse(card, session.environment.walletData)))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMissingWalletsIfNeeded(
|
||||
card: Card, session: CardSession, verifyResponse: VerifyCardResponse,
|
||||
callback: (result: CompletionResult<ScanNoteResponse>) -> Unit
|
||||
card: Card, session: CardSession,
|
||||
callback: (result: CompletionResult<ScanNoteResponse>) -> Unit,
|
||||
) {
|
||||
if (card.getStatus() == CardStatus.Empty) {
|
||||
callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse)))
|
||||
val walletData = session.environment.walletData
|
||||
if (card.wallets.isEmpty()) {
|
||||
callback(CompletionResult.Success(
|
||||
ScanNoteResponse(card, walletData)
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -92,20 +98,14 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
|
|||
val curvesToCreate = EllipticCurve.values().subtract(curvesPresent)
|
||||
|
||||
if (curvesToCreate.isEmpty()) {
|
||||
callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse)))
|
||||
callback(CompletionResult.Success(ScanNoteResponse(card, walletData)))
|
||||
return
|
||||
}
|
||||
|
||||
val configs = curvesToCreate.map { curve ->
|
||||
WalletConfig(
|
||||
isReusable = null, prohibitPurgeWallet = null, curveId = curve,
|
||||
signingMethods = null
|
||||
)
|
||||
}
|
||||
CreateWalletsTask(configs).run(session) { result ->
|
||||
CreateWalletsTask(curvesToCreate.toList()).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
callback(CompletionResult.Success(ScanNoteResponse(result.data, verifyResponse)))
|
||||
callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData)))
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
|
|
@ -113,8 +113,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
|
|||
}
|
||||
|
||||
private fun dealWithTwinCard(
|
||||
card: Card, session: CardSession, verifyResponse: VerifyCardResponse,
|
||||
callback: (result: CompletionResult<ScanNoteResponse>) -> Unit
|
||||
card: Card, session: CardSession,
|
||||
callback: (result: CompletionResult<ScanNoteResponse>) -> Unit,
|
||||
) {
|
||||
ReadIssuerDataCommand().run(session) { readDataResult ->
|
||||
when (readDataResult) {
|
||||
|
|
@ -125,12 +125,15 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
|
|||
return@run
|
||||
}
|
||||
val verified = TwinCardsManager.verifyTwinPublicKey(
|
||||
readDataResult.data.issuerData, publicKey
|
||||
readDataResult.data.issuerData, publicKey
|
||||
)
|
||||
if (verified) {
|
||||
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
|
||||
callback(CompletionResult.Success(
|
||||
ScanNoteResponse(card, verifyResponse, twinPublicKey.toHexString())
|
||||
ScanNoteResponse(
|
||||
card = card,
|
||||
walletData = session.environment.walletData,
|
||||
secondTwinPublicKey = twinPublicKey.toHexString())
|
||||
))
|
||||
return@run
|
||||
} else {
|
||||
|
|
@ -145,15 +148,21 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
|
|||
|
||||
private fun getErrorIfExcludedCard(card: Card): TangemError? {
|
||||
if (card.isExcluded()) return TapSdkError.CardForDifferentApp
|
||||
if (card.status == CardStatus.Purged) return TangemSdkError.WalletIsPurged()
|
||||
if (card.status == CardStatus.NotPersonalized) return TangemSdkError.NotPersonalized()
|
||||
// Disable new multi-currency HD wallet cards on the old version of the app
|
||||
if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard()
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getWalletManagerFactory(): WalletManagerFactory {
|
||||
val blockchainSdkConfig = store.state.globalState.configManager?.config
|
||||
?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
return WalletManagerFactory(blockchainSdkConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UpdateAppToUseThisCard : TangemError {
|
||||
override val code: Int = 50005
|
||||
override var customMessage: String = code.toString()
|
||||
override val messageResId: Int = R.string.error_update_app
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
|
||||
class TangemSignHashResponse(
|
||||
val signature: ByteArray,
|
||||
val totalSignedHashes: Int?,
|
||||
val remainingSignatures: Int?,
|
||||
) : CommandResponse
|
||||
|
||||
class SignHashTask(
|
||||
private val hash: ByteArray,
|
||||
private val walletPublicKey: ByteArray,
|
||||
) : CardSessionRunnable<TangemSignHashResponse> {
|
||||
override fun run(session: CardSession, callback: CompletionCallback<TangemSignHashResponse>) {
|
||||
SignHashCommand(hash, walletPublicKey).run(session) { response ->
|
||||
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
callback(CompletionResult.Success(TangemSignHashResponse(
|
||||
response.data.signature,
|
||||
response.data.totalSignedHashes,
|
||||
session.environment.card?.wallet(walletPublicKey)?.remainingSignatures
|
||||
)))
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Failure(response.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.sign.SignHashesCommand
|
||||
|
||||
class TangemSignHashesResponse(
|
||||
val signatures: List<ByteArray>,
|
||||
val totalSignedHashes: Int?,
|
||||
val remainingSignatures: Int?,
|
||||
) : CommandResponse
|
||||
|
||||
class SignHashesTask(
|
||||
private val hashes: Collection<ByteArray>,
|
||||
private val walletPublicKey: ByteArray,
|
||||
) : CardSessionRunnable<TangemSignHashesResponse> {
|
||||
override fun run(session: CardSession, callback: CompletionCallback<TangemSignHashesResponse>) {
|
||||
SignHashesCommand(hashes.toTypedArray(), walletPublicKey).run(session) { response ->
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
callback(CompletionResult.Success(TangemSignHashesResponse(
|
||||
response.data.signatures,
|
||||
response.data.totalSignedHashes,
|
||||
session.environment.card?.wallet(walletPublicKey)?.remainingSignatures
|
||||
)))
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Failure(response.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.domain.termsOfUse
|
|||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
|
@ -13,8 +13,8 @@ class CardTou {
|
|||
private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0)
|
||||
|
||||
fun getUrl(card: Card): Uri? {
|
||||
val issuerName = card.cardData?.issuerName ?: return null
|
||||
if (issuerName.toLowerCase() != "start2coin") return null
|
||||
val issuerName = card.issuer.name ?: return null
|
||||
if (issuerName.lowercase(Locale.getDefault()) != "start2coin") return null
|
||||
|
||||
val baseUrl = "https://app.tangem.com/tou/"
|
||||
val regionCode = regionCode(card.cardId) ?: "fr"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.squareup.moshi.JsonClass
|
|||
import com.squareup.moshi.Types
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.commands.common.card.FirmwareVersion
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.tap.common.extensions.readJsonFileToString
|
||||
import com.tangem.tap.network.createMoshi
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ class CurrenciesRepository(val context: Application) {
|
|||
cardFirmware: FirmwareVersion?,
|
||||
isTestNet: Boolean = false
|
||||
): List<Blockchain> {
|
||||
return if (cardFirmware == null || cardFirmware.major < 4) {
|
||||
return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) {
|
||||
Blockchain.secp256k1Blockchains(isTestNet)
|
||||
} else {
|
||||
Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet)
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class TopUpManager {
|
|||
) { signResponse ->
|
||||
store.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.walletSignedHashes,
|
||||
remainingSignatures = signResponse.walletRemainingSignatures,
|
||||
walletPublicKey = walletManager.wallet.publicKey
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey,
|
||||
remainingSignatures = signResponse.remainingSignatures
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.commands.wallet.CreateWalletResponse
|
||||
import com.tangem.commands.wallet.PurgeWalletCommand
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
import com.tangem.tap.domain.extensions.getDefaultWalletIndex
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.operations.wallet.CreateWalletCommand
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.PurgeWalletCommand
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tasks.CreateWalletTask
|
||||
|
||||
class CreateFirstTwinWalletTask : CardSessionRunnable<CreateWalletResponse> {
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateWalletResponse>) -> Unit,
|
||||
) {
|
||||
if (session.environment.card?.getSingleWallet()?.publicKey != null) {
|
||||
PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response ->
|
||||
val publicKey = session.environment.card?.getSingleWallet()?.publicKey
|
||||
if (publicKey != null) {
|
||||
PurgeWalletCommand(publicKey).run(session) { response ->
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
session.environment.card = session.environment.card?.changeStatusToEmpty()
|
||||
CreateWalletTask().run(session) { callback(it) }
|
||||
session.environment.card = session.environment.card?.setWallets(emptyList())
|
||||
CreateWalletCommand(EllipticCurve.Secp256k1, false)
|
||||
.run(session) { callback(it) }
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CreateWalletTask().run(session) { callback(it) }
|
||||
CreateWalletCommand(EllipticCurve.Secp256k1, false)
|
||||
.run(session) { callback(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,29 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.Message
|
||||
import com.tangem.commands.wallet.CreateWalletResponse
|
||||
import com.tangem.commands.wallet.PurgeWalletCommand
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.tap.domain.extensions.getDefaultWalletIndex
|
||||
import com.tangem.operations.wallet.CreateWalletCommand
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.PurgeWalletCommand
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tasks.CreateWalletTask
|
||||
|
||||
class CreateSecondTwinWalletTask(
|
||||
private val firstPublicKey: String,
|
||||
private val preparingMessage: Message,
|
||||
private val creatingWalletMessage: Message
|
||||
) : CardSessionRunnable<CreateWalletResponse> {
|
||||
override val requiresPin2 = true
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
|
||||
if (session.environment.card?.getSingleWallet()?.publicKey != null) {
|
||||
val publicKey = session.environment.card?.getSingleWallet()?.publicKey
|
||||
if (publicKey != null) {
|
||||
session.setInitialMessage(preparingMessage)
|
||||
PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response ->
|
||||
PurgeWalletCommand(publicKey).run(session) { response ->
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
session.environment.card = session.environment.card?.changeStatusToEmpty()
|
||||
session.environment.card = session.environment.card?.setWallets(emptyList())
|
||||
finishTask(session, callback)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error))
|
||||
|
|
@ -38,10 +36,10 @@ class CreateSecondTwinWalletTask(
|
|||
|
||||
private fun finishTask(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
|
||||
session.setInitialMessage(creatingWalletMessage)
|
||||
CreateWalletTask().run(session) { result ->
|
||||
CreateWalletCommand(EllipticCurve.Secp256k1, false).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
session.environment.card = session.environment.card?.changeStatusToLoaded()
|
||||
session.environment.card = session.environment.card?.updateWallet(result.data.wallet)
|
||||
|
||||
WriteProtectedIssuerDataTask(
|
||||
firstPublicKey.hexToBytes(), TwinCardsManager.issuerKeys
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.KeyPair
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
import com.tangem.tap.domain.tasks.ScanNoteTask
|
||||
import com.tangem.tasks.PreflightReadSettings
|
||||
import com.tangem.tasks.PreflightReadTask
|
||||
|
||||
class FinalizeTwinTask(
|
||||
private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair,
|
||||
) : CardSessionRunnable<ScanNoteResponse> {
|
||||
override val requiresPin2 = true
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
|
|
@ -21,7 +20,7 @@ class FinalizeTwinTask(
|
|||
WriteProtectedIssuerDataTask(twinPublicKey, issuerKeys).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
PreflightReadTask(PreflightReadSettings.FullCardRead).run(session) { readResult ->
|
||||
PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success ->
|
||||
ScanNoteTask(readResult.data).run(session, callback)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.KeyPair
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
|
|
@ -27,7 +27,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) {
|
|||
)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
currentCardPublicKey = response.data.walletPublicKey.toHexString()
|
||||
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
return SimpleResult.Success
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -56,7 +56,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse) {
|
|||
)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
secondCardPublicKey = response.data.walletPublicKey.toHexString()
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
return SimpleResult.Success
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardStatus
|
||||
import com.tangem.commands.common.card.masks.Product
|
||||
import com.tangem.commands.wallet.WalletStatus
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.extensions.isEven
|
||||
import com.tangem.tap.domain.twins.TwinsHelper.Companion.getTwinCardNumber
|
||||
|
||||
class TwinsHelper {
|
||||
companion object {
|
||||
|
|
@ -78,19 +76,9 @@ enum class TwinCardNumber(val number: Int) {
|
|||
}
|
||||
|
||||
fun Card.isTwinCard(): Boolean {
|
||||
return this.cardData?.productMask?.contains(Product.TwinCard) == true
|
||||
return getTwinCardNumber(cardId) != null
|
||||
}
|
||||
|
||||
fun Card.getTwinCardIdForUser(): String {
|
||||
return TwinsHelper.getTwinCardIdForUser(this.cardId)
|
||||
}
|
||||
|
||||
fun Card.changeStatusToLoaded(): Card {
|
||||
val wallets = wallets.map { it.copy(status = WalletStatus.Loaded) }
|
||||
return copy(status = CardStatus.Loaded, wallets = wallets)
|
||||
}
|
||||
|
||||
fun Card.changeStatusToEmpty(): Card {
|
||||
val wallets = wallets.map { it.copy(status = WalletStatus.Empty) }
|
||||
return copy(status = CardStatus.Empty, wallets = wallets)
|
||||
}
|
||||
|
|
@ -1,49 +1,61 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.KeyPair
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.TangemSdkConstants
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.files.FileHashHelper
|
||||
import com.tangem.tap.domain.extensions.getDefaultWalletIndex
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse
|
||||
import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
|
||||
class WriteProtectedIssuerDataTask(
|
||||
private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair
|
||||
) : CardSessionRunnable<WriteIssuerDataResponse> {
|
||||
override val requiresPin2 = true
|
||||
private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair,
|
||||
) : CardSessionRunnable<SuccessResponse> {
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<WriteIssuerDataResponse>) -> Unit) {
|
||||
val hashes = arrayOf(twinPublicKey.calculateSha256())
|
||||
SignCommand(hashes, TangemSdkConstants.getDefaultWalletIndex()).run(session) { signResult ->
|
||||
when (signResult) {
|
||||
is CompletionResult.Success -> {
|
||||
ReadIssuerDataCommand().run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success -> {
|
||||
writeIssuerData(
|
||||
twinPublicKey, issuerKeys, signResult.data.signatures[0],
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<SuccessResponse>) -> Unit,
|
||||
) {
|
||||
SignHashCommand(
|
||||
twinPublicKey.calculateSha256(),
|
||||
session.environment.card!!.getSingleWallet()!!.publicKey
|
||||
)
|
||||
.run(session) { signResult ->
|
||||
when (signResult) {
|
||||
is CompletionResult.Success -> {
|
||||
ReadIssuerDataCommand().run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success -> {
|
||||
writeIssuerData(
|
||||
twinPublicKey, issuerKeys, signResult.data.signature,
|
||||
readResult.data, session, callback
|
||||
)
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(
|
||||
readResult.error))
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error))
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeIssuerData(
|
||||
twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray,
|
||||
readResponse: ReadIssuerDataResponse,
|
||||
session: CardSession, callback: (result: CompletionResult<WriteIssuerDataResponse>
|
||||
) -> Unit) {
|
||||
twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray,
|
||||
readResponse: ReadIssuerDataResponse,
|
||||
session: CardSession,
|
||||
callback: (
|
||||
result: CompletionResult<SuccessResponse>,
|
||||
) -> Unit,
|
||||
) {
|
||||
val cardId = session.environment.card?.cardId.guard {
|
||||
callback(CompletionResult.Failure(TangemSdkError.CardError()))
|
||||
return
|
||||
|
|
@ -51,11 +63,11 @@ class WriteProtectedIssuerDataTask(
|
|||
val counter = (readResponse.issuerDataCounter ?: 0) + 1
|
||||
val data = twinPublicKey + cardSignature
|
||||
val signedByIssuer = FileHashHelper.prepareHashes(
|
||||
cardId, data, counter, issuerKeys.privateKey
|
||||
cardId, data, counter, issuerKeys.privateKey
|
||||
)
|
||||
WriteIssuerDataCommand(
|
||||
data, signedByIssuer.finalizingSignature!!,
|
||||
counter, issuerKeys.publicKey
|
||||
data, signedByIssuer.finalizingSignature!!,
|
||||
counter, issuerKeys.publicKey
|
||||
).run(session, callback)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
|
||||
|
|
@ -11,10 +10,10 @@ import com.tangem.blockchain.common.AmountType
|
|||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.extensions.*
|
||||
import com.tangem.commands.SignCommand
|
||||
import com.tangem.commands.wallet.WalletIndex
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
|
|
@ -150,9 +149,9 @@ class WalletConnectSdkHelper {
|
|||
gasLimit = null
|
||||
) ?: return null
|
||||
|
||||
val command = SignCommand(
|
||||
hashes = arrayOf(dataToSign.hash),
|
||||
walletIndex = WalletIndex.PublicKey(data.walletManager.wallet.publicKey)
|
||||
val command = SignHashCommand(
|
||||
hash = dataToSign.hash,
|
||||
walletPublicKey = data.walletManager.wallet.publicKey
|
||||
)
|
||||
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
|
||||
return when (result) {
|
||||
|
|
@ -210,13 +209,10 @@ class WalletConnectSdkHelper {
|
|||
|
||||
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
|
||||
val publicKey = wallet.walletPublicKey.hexToBytes()
|
||||
val command = SignCommand(
|
||||
arrayOf(hashToSign),
|
||||
WalletIndex.PublicKey(publicKey))
|
||||
val command = SignHashCommand(hashToSign, publicKey)
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
val hash = result.data.signatures.first()
|
||||
|
||||
val hash = result.data.signature
|
||||
return EthereumUtils.prepareSignedMessageData(
|
||||
hash, hashToSign, publicKey
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ package com.tangem.tap.features.details.redux
|
|||
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
|
|
@ -90,6 +91,8 @@ sealed class DetailsAction : Action {
|
|||
}
|
||||
|
||||
sealed class ManageSecurity : DetailsAction() {
|
||||
data class CheckCurrentSecurityOption(val cardId: String?) : ManageSecurity()
|
||||
data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity()
|
||||
object OpenSecurity : ManageSecurity()
|
||||
data class SelectOption(val option: SecurityOption) : ManageSecurity()
|
||||
object SaveChanges : ManageSecurity() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -83,10 +84,9 @@ class DetailsMiddleware {
|
|||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.EraseWallet.Confirm -> {
|
||||
val card = store.state.detailsState.card ?: return
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.eraseWallet(
|
||||
store.state.detailsState.card?.cardId
|
||||
)
|
||||
val result = tangemSdkManager.eraseWallet(card)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
|
|
@ -124,6 +124,21 @@ class DetailsMiddleware {
|
|||
class ManageSecurityMiddleware {
|
||||
fun handle(action: DetailsAction.ManageSecurity) {
|
||||
when (action) {
|
||||
is DetailsAction.ManageSecurity.CheckCurrentSecurityOption -> {
|
||||
scope.launch {
|
||||
when (val response = tangemSdkManager.checkUserCodes(action.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.ManageSecurity.SetCurrentOption(response.data)
|
||||
)
|
||||
store.dispatchOnMain(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.masks.Settings
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.tap.domain.extensions.isWalletDataSupported
|
||||
|
|
@ -47,17 +46,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
}
|
||||
|
||||
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState {
|
||||
val securityOption = when {
|
||||
action.card.isPin1Default == false -> {
|
||||
SecurityOption.AccessCode
|
||||
}
|
||||
action.card.isPin2Default == false -> {
|
||||
SecurityOption.PassCode
|
||||
}
|
||||
else -> {
|
||||
SecurityOption.LongTap
|
||||
}
|
||||
}
|
||||
val twinsState = if (action.card.isTwinCard()) {
|
||||
CreateTwinWalletState(
|
||||
scanResponse = action.scanNoteResponse,
|
||||
|
|
@ -76,7 +64,6 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta
|
|||
appCurrencyState = AppCurrencyState(
|
||||
action.fiatCurrencyName
|
||||
),
|
||||
securityScreenState = SecurityScreenState(currentOption = securityOption),
|
||||
createTwinWalletState = twinsState,
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.card)
|
||||
)
|
||||
|
|
@ -86,8 +73,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS
|
|||
return when (action) {
|
||||
DetailsAction.EraseWallet.Check -> {
|
||||
val notAllowedByCard =
|
||||
state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true
|
||||
|| state.card?.settingsMask?.contains(Settings.IsReusable) == false
|
||||
state.card?.settings?.isPermanentWallet == true
|
||||
|| state.card?.isWalletDataSupported == true
|
||||
val notEmpty = state.wallets.any {
|
||||
!it.recentTransactions.toPendingTransactions(it.address).isNullOrEmpty()
|
||||
|
|
@ -142,6 +128,20 @@ private fun handleSecurityAction(
|
|||
action: DetailsAction.ManageSecurity, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManageSecurity.SetCurrentOption -> {
|
||||
val securityOption = when {
|
||||
action.userCodes.isAccessCodeSet -> {
|
||||
SecurityOption.AccessCode
|
||||
}
|
||||
action.userCodes.isPasscodeSet -> {
|
||||
SecurityOption.PassCode
|
||||
}
|
||||
else -> {
|
||||
SecurityOption.LongTap
|
||||
}
|
||||
}
|
||||
state.copy(securityScreenState = SecurityScreenState(currentOption = securityOption))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
if (state.card?.isStart2Coin == true) {
|
||||
return state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
|
|
@ -149,13 +149,10 @@ private fun handleSecurityAction(
|
|||
selectedOption = state.securityScreenState.currentOption
|
||||
))
|
||||
}
|
||||
if (state.card?.isPin2Default == null) {
|
||||
return state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = EnumSet.noneOf(SecurityOption::class.java)
|
||||
))
|
||||
}
|
||||
|
||||
val allowedSecurityOptions = prepareAllowedSecurityOptions(state.card)
|
||||
val allowedSecurityOptions = prepareAllowedSecurityOptions(
|
||||
state.card, state.securityScreenState?.currentOption
|
||||
)
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = allowedSecurityOptions,
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
|
|
@ -177,7 +174,6 @@ private fun handleSecurityAction(
|
|||
is DetailsAction.ManageSecurity.SaveChanges.Success -> {
|
||||
// Setting options to show only LongTap from now on
|
||||
state.copy(
|
||||
card = state.card?.copy(isPin1Default = true, isPin2Default = true),
|
||||
securityScreenState = state.securityScreenState?.copy(
|
||||
currentOption = state.securityScreenState.selectedOption,
|
||||
allowedOptions = EnumSet.of(SecurityOption.LongTap)
|
||||
|
|
@ -188,19 +184,19 @@ private fun handleSecurityAction(
|
|||
}
|
||||
}
|
||||
|
||||
private fun prepareAllowedSecurityOptions(card: Card): EnumSet<SecurityOption> {
|
||||
val prohibitDefaultPin = card.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true
|
||||
val isDefaultPin1 = card.isPin1Default != false
|
||||
val isDefaultPin2 = card.isPin2Default != false
|
||||
private fun prepareAllowedSecurityOptions(
|
||||
card: Card?, currentSecurityOption: SecurityOption?
|
||||
): EnumSet<SecurityOption> {
|
||||
val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true
|
||||
|
||||
val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java)
|
||||
if ((isDefaultPin1 && isDefaultPin2) || !prohibitDefaultPin) {
|
||||
if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) {
|
||||
allowedSecurityOptions.add(SecurityOption.LongTap)
|
||||
}
|
||||
if (!isDefaultPin1) {
|
||||
if (currentSecurityOption == SecurityOption.AccessCode) {
|
||||
allowedSecurityOptions.add(SecurityOption.AccessCode)
|
||||
}
|
||||
if (!isDefaultPin2) {
|
||||
if (currentSecurityOption == SecurityOption.PassCode) {
|
||||
allowedSecurityOptions.add(SecurityOption.PassCode)
|
||||
}
|
||||
return allowedSecurityOptions
|
||||
|
|
@ -209,6 +205,6 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet<SecurityOption> {
|
|||
|
||||
private fun Card.toCardInfo(): CardInfo? {
|
||||
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
|
||||
val issuer = this.cardData?.issuerName ?: return null
|
||||
val issuer = this.issuer.name
|
||||
val signedHashes = this.signedHashesCount()
|
||||
return CardInfo(cardId, issuer, signedHashes)}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.features.details.redux
|
|||
|
||||
import android.net.Uri
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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.card.Card
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
}
|
||||
|
||||
tv_security_title.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.card?.cardId))
|
||||
}
|
||||
|
||||
val currentSecurity = when (state.securityScreenState?.currentOption) {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ import androidx.core.content.FileProvider
|
|||
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.common.card.Card
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.domain.TapWorkarounds
|
||||
import timber.log.Timber
|
||||
|
|
@ -176,11 +175,10 @@ class AdditionalEmailInfo {
|
|||
|
||||
fun setCardInfo(card: Card) {
|
||||
cardId = card.cardId
|
||||
cardFirmwareVersion = card.firmwareVersion.version
|
||||
cardIssuer = card.cardData?.issuerName ?: ""
|
||||
cardFirmwareVersion = card.firmwareVersion.stringValue
|
||||
cardIssuer = card.issuer.name
|
||||
signedHashesCount = card.wallets
|
||||
.filter { it.status == WalletStatus.Loaded }
|
||||
.joinToString(";") { "${it.curve?.curve} - ${it.signedHashes}" }
|
||||
.joinToString(";") { "${it.curve?.curve} - ${it.totalSignedHashes}" }
|
||||
}
|
||||
|
||||
fun setWalletsInfo(walletManagers: List<WalletManager>) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.isPayIdSupported
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.tap.features.send.redux.middlewares
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
|
|
@ -136,9 +136,9 @@ private fun sendTransaction(
|
|||
) { signResponse ->
|
||||
store.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.walletSignedHashes,
|
||||
remainingSignatures = signResponse.walletRemainingSignatures,
|
||||
walletPublicKey = walletManager.wallet.publicKey
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey,
|
||||
remainingSignatures = signResponse.remainingSignatures
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -150,7 +150,7 @@ private fun sendTransaction(
|
|||
FirebaseAnalyticsHandler.triggerEvent(
|
||||
event = AnalyticsEvent.TRANSACTION_IS_SENT,
|
||||
card = card,
|
||||
blockchain = walletManager.wallet.blockchain
|
||||
blockchain = walletManager.wallet.blockchain.currency
|
||||
)
|
||||
dispatch(SendAction.SendSuccess)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux
|
|||
import android.content.Context
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
|
|
@ -93,6 +93,8 @@ sealed class WalletAction : Action {
|
|||
object Failure : WalletAction()
|
||||
}
|
||||
|
||||
class LoadCardInfo(val card: Card) : WalletAction()
|
||||
|
||||
data class LoadArtwork(val card: Card, val artworkId: String?) : WalletAction() {
|
||||
data class Success(val artwork: Artwork) : WalletAction()
|
||||
object Failure : WalletAction()
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ package com.tangem.tap.features.wallet.redux.middlewares
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.shareText
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -155,6 +158,32 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadCardInfo -> {
|
||||
scope.launch {
|
||||
val response = OnlineCardVerifier().getCardInfo(
|
||||
action.card.cardId, action.card.cardPublicKey
|
||||
)
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
store.dispatchOnMain(
|
||||
WalletAction.SetArtworkId(response.data.artwork?.id)
|
||||
)
|
||||
store.dispatchOnMain(
|
||||
WalletAction.LoadArtwork(action.card, response.data.artwork?.id)
|
||||
)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.SetIfCardVerifiedOnline(response.data.passed)
|
||||
)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.SetIfCardVerifiedOnline(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
val scanNoteResponse = globalState?.scanNoteResponse
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ import com.tangem.blockchain.common.BlockchainSdkError
|
|||
import com.tangem.blockchain.common.SignatureCountValidator
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.commands.common.card.Card
|
||||
import com.tangem.commands.common.card.CardType
|
||||
import com.tangem.commands.verification.VerifyCardState
|
||||
import com.tangem.common.extensions.getType
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.isGreaterThan
|
||||
|
|
@ -91,15 +89,13 @@ class WarningsMiddleware {
|
|||
}
|
||||
|
||||
showWarningLowRemainingSignaturesIfNeeded(card)
|
||||
if (card.getType() != CardType.Release) {
|
||||
if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) {
|
||||
addWarningMessage(WarningMessagesManager.devCardWarning())
|
||||
} else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
|
||||
checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) }
|
||||
}
|
||||
if (card.getType() == CardType.Release) {
|
||||
if (globalState.scanNoteResponse.verifyResponse?.verificationState ==
|
||||
VerifyCardState.VerifiedOffline
|
||||
) {
|
||||
if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) {
|
||||
if (!globalState.cardVerifiedOnline) {
|
||||
addWarningMessage(WarningMessagesManager.onlineVerificationFailed())
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +157,7 @@ class WarningsMiddleware {
|
|||
val validator = store.state.walletState.walletManagers.firstOrNull()
|
||||
as? SignatureCountValidator
|
||||
scope.launch {
|
||||
val signedHashes = card.getSingleWallet()?.signedHashes ?: 0
|
||||
val signedHashes = card.getSingleWallet()?.totalSignedHashes ?: 0
|
||||
val result = validator?.validateSignatureCount(signedHashes)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.tap.features.wallet.redux.reducers
|
|||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.common.network.TangemService
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
|
|
@ -216,21 +216,27 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState)
|
||||
is WalletAction.LoadArtwork -> {
|
||||
val cardId = action.card.cardId
|
||||
val cardPublicKey = action.card.cardPublicKey?.toHexString()
|
||||
val artworkUrl = if (cardPublicKey != null && action.artworkId != null) {
|
||||
TangemService.getUrlForArtwork(cardId, cardPublicKey, action.artworkId)
|
||||
} else if (action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID)) {
|
||||
Artwork.SERGIO_CARD_URL
|
||||
} else if (action.card.cardId.startsWith(Artwork.MARTA_CARD_ID)) {
|
||||
Artwork.MARTA_CARD_URL
|
||||
} else if (newState.twinCardsState?.cardNumber != null) {
|
||||
when (newState.twinCardsState?.cardNumber) {
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
|
||||
null -> Artwork.DEFAULT_IMG_URL
|
||||
val cardPublicKey = action.card.cardPublicKey.toHexString()
|
||||
val artworkUrl = when {
|
||||
action.artworkId != null -> {
|
||||
OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey, action.artworkId)
|
||||
}
|
||||
action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID) -> {
|
||||
Artwork.SERGIO_CARD_URL
|
||||
}
|
||||
action.card.cardId.startsWith(Artwork.MARTA_CARD_ID) -> {
|
||||
Artwork.MARTA_CARD_URL
|
||||
}
|
||||
newState.twinCardsState?.cardNumber != null -> {
|
||||
when (newState.twinCardsState?.cardNumber) {
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
|
||||
null -> Artwork.DEFAULT_IMG_URL
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Artwork.DEFAULT_IMG_URL
|
||||
}
|
||||
} else {
|
||||
Artwork.DEFAULT_IMG_URL
|
||||
}
|
||||
newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.network.coinmarketcap
|
||||
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.performRequest
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.tap.network.moonpay
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.performRequest
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.tap.network.createRetrofitInstance
|
||||
|
||||
class MoonpayService {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.performRequest
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.tap.network.createRetrofitInstance
|
||||
import retrofit2.Retrofit
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.performRequest
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.tap.network.createRetrofitInstance
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -166,4 +166,6 @@ this wallet.
|
|||
|
||||
|
||||
<string name="wallet_dialog_choose_trade_action" translatable="false">Do you want to buy or sell crypto?</string>
|
||||
|
||||
<string name="error_update_app" translatable="false">Oops, the current version of the application is not ready to work with this card, please check for updates.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
ext.versions = [
|
||||
kotlin : '1.5.20',
|
||||
build_gradle: '4.2.2',
|
||||
build_gradle: '7.0.0',
|
||||
]
|
||||
|
|
|
|||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue