Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-17 16:39:05 +03:00
parent 587a0c8624
commit 88cf7ec1af
212 changed files with 1223 additions and 1128 deletions

View file

@ -5,6 +5,39 @@
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="5" /> <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="5" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="3" /> <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="3" />
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="android" withSubpackages="true" static="true" />
<package name="androidx" withSubpackages="true" static="true" />
<package name="com" withSubpackages="true" static="true" />
<package name="junit" withSubpackages="true" static="true" />
<package name="net" withSubpackages="true" static="true" />
<package name="org" withSubpackages="true" static="true" />
<package name="java" withSubpackages="true" static="true" />
<package name="javax" withSubpackages="true" static="true" />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="androidx" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings> </JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> <option name="PACKAGES_TO_USE_STAR_IMPORTS">

View file

@ -263,7 +263,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
tangemSdkManager = injectedTangemSdkManager tangemSdkManager = injectedTangemSdkManager
backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this) backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this)
backupService = backupServiceHolder.backupService.get()!! // will be deleted eventually backupService = requireNotNull(backupServiceHolder.backupService.get()) // will be deleted eventually
lockUserWalletsTimer = LockUserWalletsTimer( lockUserWalletsTimer = LockUserWalletsTimer(
context = this, context = this,
@ -370,8 +370,8 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
val fromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) ?: false val isFromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) == true
if (fromPush) { if (isFromPush) {
analyticsEventsHandler.send(Push.PushNotificationOpened) analyticsEventsHandler.send(Push.PushNotificationOpened)
} }
@ -402,8 +402,8 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
} }
override fun dispatchTouchEvent(event: MotionEvent): Boolean { override fun dispatchTouchEvent(event: MotionEvent): Boolean {
val result = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler) val isHandled = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler)
return if (result) super.dispatchTouchEvent(event) else false return if (isHandled) super.dispatchTouchEvent(event) else false
} }
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
@ -447,9 +447,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
getPolkadotCheckHasImmortalUseCase() getPolkadotCheckHasImmortalUseCase()
.flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED) .flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED)
.distinctUntilChanged() .distinctUntilChanged()
.collect { .collect { (_, hasImmortalTransaction) ->
analyticsEventsHandler.send( analyticsEventsHandler.send(
WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(it.second), WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(hasImmortalTransaction),
) )
} }
} }

View file

@ -88,7 +88,7 @@ lateinit var store: Store<AppState>
lateinit var foregroundActivityObserver: ForegroundActivityObserver lateinit var foregroundActivityObserver: ForegroundActivityObserver
internal lateinit var derivationsFinder: DerivationsFinder internal lateinit var derivationsFinder: DerivationsFinder
abstract class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider { open class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider {
// region DI // region DI
private val entryPoint: ApplicationEntryPoint private val entryPoint: ApplicationEntryPoint

View file

@ -7,7 +7,7 @@ package com.tangem.tap.common
object TestActions { object TestActions {
// It used only for the test actions in debug or debug_beta builds // It used only for the test actions in debug or debug_beta builds
var testAmountInjectionForWalletManagerEnabled = false var isTestAmountInjectionForWalletManagerEnabled = false
} }
typealias TestAction = Pair<String, () -> Unit> typealias TestAction = Pair<String, () -> Unit>

View file

@ -58,7 +58,7 @@ private class BlockchainSdkErrorConverter(
if (value.customMessage.contains(DemoTransactionSender.ID)) return emptyMap() if (value.customMessage.contains(DemoTransactionSender.ID)) return emptyMap()
if (value is BlockchainSdkError.WrappedTangemError) { if (value is BlockchainSdkError.WrappedTangemError) {
return (value.tangemError as? TangemSdkError)?.let { cardSdkErrorConverter.convert(it) } ?: emptyMap() return (value.tangemError as? TangemSdkError)?.let { cardSdkErrorConverter.convert(it) }.orEmpty()
} }
return mapOf( return mapOf(

View file

@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
*/ */
sealed class Chat( sealed class Chat(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Chat", event, params) { ) : AnalyticsEvent("Chat", event, params) {
class ScreenOpened : Chat("Chat Screen Opened") class ScreenOpened : Chat("Chat Screen Opened")

View file

@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
sealed class Onboarding( sealed class Onboarding(
category: String, category: String,
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) { ) : AnalyticsEvent(category, event, params) {
class Started : Onboarding("Onboarding", "Onboarding Started") class Started : Onboarding("Onboarding", "Onboarding Started")
@ -16,7 +16,7 @@ sealed class Onboarding(
sealed class CreateWallet( sealed class CreateWallet(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Create Wallet", event, params) { ) : Onboarding("Onboarding / Create Wallet", event, params) {
class ScreenOpened : CreateWallet("Create Wallet Screen Opened") class ScreenOpened : CreateWallet("Create Wallet Screen Opened")
@ -36,7 +36,7 @@ sealed class Onboarding(
sealed class Backup( sealed class Backup(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Backup", event, params) { ) : Onboarding("Onboarding / Backup", event, params) {
class ScreenOpened : Backup("Backup Screen Opened") class ScreenOpened : Backup("Backup Screen Opened")
@ -64,7 +64,7 @@ sealed class Onboarding(
sealed class Topup( sealed class Topup(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Top Up", event, params) { ) : Onboarding("Onboarding / Top Up", event, params) {
class ScreenOpened : Topup("Activation Screen Opened") class ScreenOpened : Topup("Activation Screen Opened")
@ -79,7 +79,7 @@ sealed class Onboarding(
sealed class Twins( sealed class Twins(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Twins", event, params) { ) : Onboarding("Onboarding / Twins", event, params) {
class ScreenOpened : Twins("Twinning Screen Opened") class ScreenOpened : Twins("Twinning Screen Opened")

View file

@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
sealed class Settings( sealed class Settings(
category: String = "Settings", category: String = "Settings",
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) { ) : AnalyticsEvent(category, event, params) {
class ScreenOpened : Settings(event = "Settings Screen Opened") class ScreenOpened : Settings(event = "Settings Screen Opened")
@ -17,7 +17,7 @@ sealed class Settings(
sealed class CardSettings( sealed class CardSettings(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Settings("Settings / Card Settings", event, params) { ) : Settings("Settings / Card Settings", event, params) {
class ButtonFactoryReset : CardSettings("Button - Factory Reset") class ButtonFactoryReset : CardSettings("Button - Factory Reset")
@ -54,7 +54,7 @@ sealed class Settings(
sealed class AppSettings( sealed class AppSettings(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Settings(category = "Settings / App Settings", event = event, params = params) { ) : Settings(category = "Settings / App Settings", event = event, params = params) {
class SaveWalletSwitcherChanged(state: AnalyticsParam.OnOffState) : AppSettings( class SaveWalletSwitcherChanged(state: AnalyticsParam.OnOffState) : AppSettings(

View file

@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
*/ */
sealed class SignIn( sealed class SignIn(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Sign In", event, params) { ) : AnalyticsEvent("Sign In", event, params) {
class ScreenOpened : SignIn(event = "Sign In Screen Opened") class ScreenOpened : SignIn(event = "Sign In Screen Opened")

View file

@ -9,12 +9,12 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
sealed class Token( sealed class Token(
category: String, category: String,
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) { ) : AnalyticsEvent(category, event, params) {
sealed class Receive( sealed class Receive(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Token("Token / Receive", event, params) { ) : Token("Token / Receive", event, params) {
class ScreenOpened( class ScreenOpened(
@ -29,7 +29,7 @@ sealed class Token(
sealed class Topup( sealed class Topup(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Token("Token / Topup", event, params) { ) : Token("Token / Topup", event, params) {
class ScreenOpened : Topup("Top Up Screen Opened") class ScreenOpened : Topup("Top Up Screen Opened")
@ -38,7 +38,7 @@ sealed class Token(
sealed class Withdraw( sealed class Withdraw(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : Token("Token / Withdraw", event, params) { ) : Token("Token / Withdraw", event, params) {
class ScreenOpened : Withdraw("Withdraw Screen Opened") class ScreenOpened : Withdraw("Withdraw Screen Opened")

View file

@ -19,6 +19,7 @@ internal class DefaultClipboardManager(private val clipboardManager: AndroidClip
clipboardManager.setPrimaryClip(clip) clipboardManager.setPrimaryClip(clip)
} }
@Suppress("UseIsNullOrEmpty")
override fun getText(default: String?): String? { override fun getText(default: String?): String? {
val clip = clipboardManager.primaryClip val clip = clipboardManager.primaryClip

View file

@ -90,6 +90,6 @@ fun Store<AppState>.dispatchNavigationAction(action: AppRouter.() -> Unit) {
inline fun <reified T> Store<AppState>.inject(getDependency: DaggerGraphState.() -> T?): T { inline fun <reified T> Store<AppState>.inject(getDependency: DaggerGraphState.() -> T?): T {
return requireNotNull(state.daggerGraphState.getDependency()) { return requireNotNull(state.daggerGraphState.getDependency()) {
"${T::class.simpleName} isn't initialized " "${T::class.simpleName.orEmpty()} isn't initialized "
} }
} }

View file

@ -22,9 +22,9 @@ import timber.log.Timber
) )
@Suppress("MagicNumber") @Suppress("MagicNumber")
suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result<Wallet> = try { suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result<Wallet> = try {
if (isDemoCard || TestActions.testAmountInjectionForWalletManagerEnabled) { if (isDemoCard || TestActions.isTestAmountInjectionForWalletManagerEnabled) {
delay(500) delay(500)
TestActions.testAmountInjectionForWalletManagerEnabled = false TestActions.isTestAmountInjectionForWalletManagerEnabled = false
Result.Success(wallet) Result.Success(wallet)
} else { } else {
update() update()
@ -35,7 +35,7 @@ suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result<Wallet> = try
val networkConnectionManager = store.inject(DaggerGraphState::networkConnectionManager) val networkConnectionManager = store.inject(DaggerGraphState::networkConnectionManager)
if (!networkConnectionManager.isOnline) { if (!networkConnectionManager.isOnline) {
Result.Failure(TapError.NoInternetConnection) Result.Failure(TapError.NoInternetConnection())
} else { } else {
val blockchain = wallet.blockchain val blockchain = wallet.blockchain
val amountToCreateAccount = blockchain.amountToCreateAccount(this, wallet.getFirstToken()) val amountToCreateAccount = blockchain.amountToCreateAccount(this, wallet.getFirstToken())

View file

@ -44,6 +44,7 @@ class TangemAppLoggerInitializer(
} }
} }
@Suppress("BooleanPropertyNaming")
private companion object { private companion object {
val IS_LOG_ENABLED: Boolean = BuildConfig.LOG_ENABLED val IS_LOG_ENABLED: Boolean = BuildConfig.LOG_ENABLED
val PERMITTED_PRIORITY = listOf(Log.ERROR, Log.INFO) val PERMITTED_PRIORITY = listOf(Log.ERROR, Log.INFO)

View file

@ -20,6 +20,6 @@ data class GlobalState(
typealias CryptoCurrencyName = String typealias CryptoCurrencyName = String
data class OnboardingState( data class OnboardingState(
val onboardingStarted: Boolean = false, val isOnboardingStarted: Boolean = false,
val shouldResetOnCreate: Boolean = false, val shouldResetOnCreate: Boolean = false,
) )

View file

@ -61,9 +61,9 @@ internal class AndroidEmailSender : EmailSender {
.setSubject(email.subject) .setSubject(email.subject)
.setText(email.message) .setText(email.message)
email.attachment?.let { email.attachment?.let { file ->
builder.setStream( builder.setStream(
FileProvider.getUriForFile(activity, "${activity.packageName}.provider", it), FileProvider.getUriForFile(activity, "${activity.packageName}.provider", file),
) )
} }

View file

@ -39,7 +39,7 @@ class TangemSigner(
TangemSignerResponse( TangemSignerResponse(
totalSignedHashes = result.data.totalSignedHashes, totalSignedHashes = result.data.totalSignedHashes,
remainingSignatures = result.data.remainingSignatures, remainingSignatures = result.data.remainingSignatures,
isRing = result.data.batchId?.let(::isRing) ?: false, isRing = result.data.batchId?.let(::isRing) == true,
), ),
) )
if (continuation.isActive) { if (continuation.isActive) {
@ -85,7 +85,7 @@ class TangemSigner(
TangemSignerResponse( TangemSignerResponse(
totalSignedHashes = result.data.totalSignedHashes, totalSignedHashes = result.data.totalSignedHashes,
remainingSignatures = result.data.remainingSignatures, remainingSignatures = result.data.remainingSignatures,
isRing = result.data.batchId?.let(::isRing) ?: false, isRing = result.data.batchId?.let(::isRing) == true,
), ),
) )
if (continuation.isActive) { if (continuation.isActive) {

View file

@ -20,23 +20,23 @@ sealed class TapError(
override val args: List<Any>? = null, override val args: List<Any>? = null,
) : Throwable(), TapErrors, ArgError { ) : Throwable(), TapErrors, ArgError {
object UnknownError : TapError(R.string.send_error_unknown) class UnknownError : TapError(R.string.send_error_unknown)
open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage))
object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) class NoInternetConnection : TapError(R.string.wallet_notification_no_internet)
sealed class WalletManager { sealed class WalletManager {
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
class InternalError(message: String) : CustomError(message) class InternalError(message: String) : CustomError(message)
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) class BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
} }
} }
sealed class TapSdkError(override val messageResId: Int?) : TangemError(code = 50100) { sealed class TapSdkError(override val messageResId: Int?) : TangemError(code = 50100) {
override var customMessage: String = code.toString() override var customMessage: String = code.toString()
object CardForDifferentApp : TapSdkError(R.string.alert_unsupported_card) class CardForDifferentApp : TapSdkError(R.string.alert_unsupported_card)
object CardNotSupportedByRelease : TapSdkError(R.string.error_wrong_card_type) class CardNotSupportedByRelease : TapSdkError(R.string.error_wrong_card_type)
} }
fun TapErrors.assembleErrors(): MutableList<Pair<Int, List<Any>?>> { fun TapErrors.assembleErrors(): MutableList<Pair<Int, List<Any>?>> {

View file

@ -70,7 +70,7 @@ internal class DefaultResetCardUseCase(
null null
} }
type?.let { if (type != null) {
tangemSdkManager.setUserCodeRequestPolicy(policy = UserCodeRequestPolicy.Always(type)) tangemSdkManager.setUserCodeRequestPolicy(policy = UserCodeRequestPolicy.Always(type))
} }
} }

View file

@ -119,14 +119,14 @@ internal class LegacyScanProcessor @Inject constructor(
} }
private fun sendAnalytics(analyticsEvent: AnalyticsEvent?, scanResponse: ScanResponse) { private fun sendAnalytics(analyticsEvent: AnalyticsEvent?, scanResponse: ScanResponse) {
analyticsEvent?.let { analyticsEvent?.let { event ->
// this workaround needed to send CardWasScannedEvent without adding a context // this workaround needed to send CardWasScannedEvent without adding a context
val interceptor = CardContextInterceptor(scanResponse) val interceptor = CardContextInterceptor(scanResponse)
val params = it.params.toMutableMap() val params = event.params.toMutableMap()
interceptor.intercept(params) interceptor.intercept(params)
it.params = params.toMap() event.params = params.toMap()
Analytics.send(it) Analytics.send(event)
} }
} }

View file

@ -33,8 +33,8 @@ internal object UseCaseScanProcessor {
return scanCardUseCase(cardId, allowsRequestAccessCodeFromRepository) return scanCardUseCase(cardId, allowsRequestAccessCodeFromRepository)
.fold( .fold(
ifLeft = { ifLeft = { scanCardException ->
val error = scanCardExceptionConverter.convertBack(it) val error = scanCardExceptionConverter.convertBack(scanCardException)
Analytics.sendErrorEvent(TangemSdkErrorEvent(error)) Analytics.sendErrorEvent(TangemSdkErrorEvent(error))
CompletionResult.Failure(error) CompletionResult.Failure(error)

View file

@ -8,10 +8,10 @@ sealed class ScanChainException : ScanCardException.ChainException() {
/** /**
* May be returned from [DisclaimerChain] * May be returned from [DisclaimerChain]
* */ * */
data object DisclaimerWasCanceled : ScanChainException() { class DisclaimerWasCanceled : ScanChainException() {
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
private fun readResolve(): Any = DisclaimerWasCanceled private fun readResolve(): Any = DisclaimerWasCanceled()
} }
/** /**

View file

@ -13,19 +13,19 @@ object MockProvider {
private var content: MockContent = getMockContent(ProductType.Wallet) private var content: MockContent = getMockContent(ProductType.Wallet)
private var emulateError: Boolean = false private var isEmulatingError: Boolean = false
private var emulatedError: TangemError = TangemSdkError.TagLost() private var emulatedError: TangemError = TangemSdkError.TagLost()
fun setEmulateError(error: TangemError? = null) { fun setEmulateError(error: TangemError? = null) {
emulateError = true isEmulatingError = true
error?.let { error?.let {
emulatedError = it emulatedError = it
} }
} }
fun resetEmulateError() { fun resetEmulateError() {
emulateError = false isEmulatingError = false
} }
fun setMocks(productType: ProductType) { fun setMocks(productType: ProductType) {
@ -74,7 +74,7 @@ object MockProvider {
} }
private fun <T> CompletionResult.Success<T>.orFailure(): CompletionResult<T> { private fun <T> CompletionResult.Success<T>.orFailure(): CompletionResult<T> {
return if (emulateError) { return if (isEmulatingError) {
CompletionResult.Failure(emulatedError) CompletionResult.Failure(emulatedError)
} else { } else {
this this

View file

@ -168,52 +168,52 @@ object BackupWalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey(
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
) )
@ -234,24 +234,24 @@ object BackupWalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
primaryCard = primaryCard, primaryCard = primaryCard,
) )

View file

@ -168,52 +168,52 @@ object DevWalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey(
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
) )
@ -234,24 +234,24 @@ object DevWalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
primaryCard = primaryCard, primaryCard = primaryCard,
) )

View file

@ -219,38 +219,38 @@ object Wallet2MockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
) )
@ -271,24 +271,24 @@ object Wallet2MockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
primaryCard = primaryCard, primaryCard = primaryCard,
) )

View file

@ -219,38 +219,38 @@ object Wallet2WithSeedPhraseMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
) )
@ -271,24 +271,24 @@ object Wallet2WithSeedPhraseMockContent : MockContent {
byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
primaryCard = primaryCard, primaryCard = primaryCard,
) )

View file

@ -168,59 +168,59 @@ object WalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey( // xrp DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey( // xrp
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey(
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
) )
@ -241,24 +241,24 @@ object WalletMockContent : MockContent {
byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
) )
to to
ExtendedPublicKeysMap( ExtendedPublicKeysMap(
mapOf( mapOf(
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
), ),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
depth = 0, depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0), parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0, childNumber = 0,
),
), ),
), ),
),
), ),
primaryCard = primaryCard, primaryCard = primaryCard,
) )

View file

@ -42,7 +42,12 @@ class SignHashesTask(
is CompletionResult.Failure -> { is CompletionResult.Failure -> {
when { when {
response.error is TangemSdkError.WalletNotFound && pairWalletPublicKey != null -> { response.error is TangemSdkError.WalletNotFound && pairWalletPublicKey != null -> {
sign(session, pairWalletPublicKey, publicKey.derivationPath, callback) sign(
session = session,
publicKey = pairWalletPublicKey,
derivationPath = publicKey.derivationPath,
callback = callback,
)
} }
else -> callback(CompletionResult.Failure(response.error)) else -> callback(CompletionResult.Failure(response.error))
} }

View file

@ -64,22 +64,28 @@ class CreateProductWalletTask(
cardTypesResolver.isTangemTwins() -> cardTypesResolver.isTangemTwins() ->
throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
else -> CreateWalletTangemWallet(mnemonic, passphrase, shouldReset, derivationStyleProvider, cardDto) else -> CreateWalletTangemWallet(
mnemonic = mnemonic,
passphrase = passphrase,
shouldReset = shouldReset,
derivationStyleProvider = derivationStyleProvider,
cardDTO = cardDto,
)
} }
commandProcessor.proceed(cardDto, session) { commandProcessor.proceed(cardDto, session) { result ->
when (it) { when (result) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
val result = when (commandProcessor) { val createProductWalletTaskResponse = when (commandProcessor) {
is CreateWalletTangemWallet -> { is CreateWalletTangemWallet -> {
it.data as CreateProductWalletTaskResponse result.data as CreateProductWalletTaskResponse
} }
else -> CreateProductWalletTaskResponse(card = session.environment.card!!) else -> CreateProductWalletTaskResponse(card = requireNotNull(session.environment.card))
} }
callback(CompletionResult.Success(result)) callback(CompletionResult.Success(createProductWalletTaskResponse))
} }
is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
} }
} }
} }
@ -156,7 +162,12 @@ private class CreateWalletTangemWallet(
CreateWalletsTask(cardConfig.mandatoryCurves, mnemonic, passphrase).run(session) { result -> CreateWalletsTask(cardConfig.mandatoryCurves, mnemonic, passphrase).run(session) { result ->
when (result) { when (result) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
checkIfAllWalletsCreated(card, session, result.data, callback) checkIfAllWalletsCreated(
card = card,
session = session,
createResponse = result.data,
callback = callback,
)
} }
is CompletionResult.Failure -> { is CompletionResult.Failure -> {
callback(CompletionResult.Failure(result.error)) callback(CompletionResult.Failure(result.error))
@ -210,12 +221,16 @@ private class CreateWalletTangemWallet(
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit, callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
) { ) {
val resetCommand = ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository = false) val resetCommand = ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository = false)
resetCommand.run(session) { resetCommand.run(session) { result ->
when (it) { when (result) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
createMultiWallet(card, session, callback) createMultiWallet(
card = card,
session = session,
callback = callback,
)
} }
is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
} }
} }
} }
@ -228,17 +243,27 @@ private class CreateWalletTangemWallet(
) { ) {
when { when {
card.settings.isBackupAllowed -> { card.settings.isBackupAllowed -> {
linkPrimaryCard(card, createWalletResponses, session, callback) linkPrimaryCard(
card = card,
createWalletResponses = createWalletResponses,
session = session,
callback = callback,
)
} }
card.settings.isHDWalletAllowed -> { card.settings.isHDWalletAllowed -> {
deriveKeys(card, createWalletResponses, session, callback) deriveKeys(
card = card,
createWalletResponses = createWalletResponses,
session = session,
callback = callback,
)
} }
else -> { else -> {
callback( callback(
CompletionResult.Success( CompletionResult.Success(
CreateProductWalletTaskResponse(card = session.environment.card!!), CreateProductWalletTaskResponse(card = requireNotNull(session.environment.card)),
), ),
) )
} }
@ -247,7 +272,7 @@ private class CreateWalletTangemWallet(
private fun linkPrimaryCard( private fun linkPrimaryCard(
card: CardDTO, card: CardDTO,
createWalletResponse: List<CreateWalletResponse>, createWalletResponses: List<CreateWalletResponse>,
session: CardSession, session: CardSession,
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit, callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
) { ) {
@ -257,14 +282,19 @@ private class CreateWalletTangemWallet(
primaryCard = result.data primaryCard = result.data
when { when {
card.settings.isHDWalletAllowed -> { card.settings.isHDWalletAllowed -> {
deriveKeys(card, createWalletResponse, session, callback) deriveKeys(
card = card,
createWalletResponses = createWalletResponses,
session = session,
callback = callback,
)
} }
else -> { else -> {
callback( callback(
CompletionResult.Success( CompletionResult.Success(
CreateProductWalletTaskResponse( CreateProductWalletTaskResponse(
card = session.environment.card!!, card = requireNotNull(session.environment.card),
primaryCard = primaryCard, primaryCard = primaryCard,
), ),
), ),
@ -282,13 +312,13 @@ private class CreateWalletTangemWallet(
private fun deriveKeys( private fun deriveKeys(
card: CardDTO, card: CardDTO,
createWalletResponse: List<CreateWalletResponse>, createWalletResponses: List<CreateWalletResponse>,
session: CardSession, session: CardSession,
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit, callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
) { ) {
val map = mutableMapOf<ByteArrayKey, List<DerivationPath>>() val map = mutableMapOf<ByteArrayKey, List<DerivationPath>>()
var isBlockchainsForCurvesExist = false var isBlockchainsForCurvesExist = false
createWalletResponse.forEach { response -> createWalletResponses.forEach { response ->
val blockchainsForCurve = getBlockchains(response.cardId, card).filter { val blockchainsForCurve = getBlockchains(response.cardId, card).filter {
it.getSupportedCurves().contains(response.wallet.curve) it.getSupportedCurves().contains(response.wallet.curve)
} }

View file

@ -42,7 +42,7 @@ class CreateWalletsTask(
callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit, callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit,
) { ) {
val extendedPrivateKey = mnemonic?.let { val extendedPrivateKey = mnemonic?.let {
AnyMasterKeyFactory(mnemonic = it, passphrase = passphrase ?: "").makeMasterKey(curve) AnyMasterKeyFactory(mnemonic = it, passphrase = passphrase.orEmpty()).makeMasterKey(curve)
} }
CreateWalletTask(curve, extendedPrivateKey).run(session) { result -> CreateWalletTask(curve, extendedPrivateKey).run(session) { result ->
when (result) { when (result) {

View file

@ -34,12 +34,10 @@ internal class DerivationsFinder(
val userWalletId = UserWalletIdBuilder.card(card).build() ?: return emptySet() val userWalletId = UserWalletIdBuilder.card(card).build() ?: return emptySet()
val derivationStyle = derivationStyleProvider.getDerivationStyle() val derivationStyle = derivationStyleProvider.getDerivationStyle()
var blockchains = withContext(dispatchers.io) { val blockchains = withContext(dispatchers.io) {
getBlockchains(userWalletId) getBlockchains(userWalletId)
} }.ifEmpty {
if (DemoHelper.isDemoCardId(card.cardId)) {
if (blockchains.isEmpty()) {
blockchains = if (DemoHelper.isDemoCardId(card.cardId)) {
getDemoBlockchains(derivationStyle) getDemoBlockchains(derivationStyle)
} else { } else {
getDefaultBlockchains(derivationStyle) getDefaultBlockchains(derivationStyle)

View file

@ -19,10 +19,15 @@ class ResetToFactorySettingsTask(
} }
private fun deleteWallets(session: CardSession, callback: (result: CompletionResult<Boolean>) -> Unit) { private fun deleteWallets(session: CardSession, callback: (result: CompletionResult<Boolean>) -> Unit) {
val wallet = session.environment.card?.wallets?.lastOrNull().guard { val wallet = session
resetBackup(session, callback) .environment
return .card
} ?.wallets
?.lastOrNull()
.guard {
resetBackup(session, callback)
return
}
PurgeWalletCommand(wallet.publicKey).run(session) { result -> PurgeWalletCommand(wallet.publicKey).run(session) { result ->
when (result) { when (result) {

View file

@ -111,13 +111,13 @@ internal class ScanProductTask(
} }
private fun getErrorIfExcludedCard(cardDto: CardDTO, card: Card): TangemError? { private fun getErrorIfExcludedCard(cardDto: CardDTO, card: Card): TangemError? {
if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp()
if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease()
// according ios decline card lower Ed25519Slip0010Available version and contains imported wallets // according ios decline card lower Ed25519Slip0010Available version and contains imported wallets
if (card.firmwareVersion < FirmwareVersion.Ed25519Slip0010Available && if (card.firmwareVersion < FirmwareVersion.Ed25519Slip0010Available &&
card.wallets.any { it.isImported } card.wallets.any { it.isImported }
) { ) {
return TapSdkError.CardNotSupportedByRelease return TapSdkError.CardNotSupportedByRelease()
} }
return null return null
} }
@ -253,12 +253,13 @@ private class ScanWalletProcessor(
callback: (result: CompletionResult<ScanResponse>) -> Unit, callback: (result: CompletionResult<ScanResponse>) -> Unit,
) { ) {
mainScope.launch { mainScope.launch {
val activationInProgress = store.inject(DaggerGraphState::cardRepository) val isActivationInProgress = store.inject(DaggerGraphState::cardRepository)
.isActivationInProgress(card.cardId) .isActivationInProgress(card.cardId)
@Suppress("ComplexCondition") @Suppress("ComplexCondition")
if (card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty() && if (card.backupStatus == CardDTO.BackupStatus.NoBackup &&
activationInProgress card.wallets.isNotEmpty() &&
isActivationInProgress
) { ) {
StartPrimaryCardLinkingTask().run(session) { linkingResult -> StartPrimaryCardLinkingTask().run(session) { linkingResult ->
when (linkingResult) { when (linkingResult) {
@ -372,8 +373,8 @@ private class ScanTwinProcessor : ProductCommandProcessor<ScanResponse> {
return@run return@run
} }
val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) val isVerified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
val response = if (verified) { val response = if (isVerified) {
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
val walletData = session.environment.walletData val walletData = session.environment.walletData
ScanResponse( ScanResponse(

View file

@ -153,9 +153,9 @@ class VisaCardActivationTask @AssistedInject constructor(
val otpTaskDeferred = async { createOTP() } val otpTaskDeferred = async { createOTP() }
val dataToSign = dataToSignDeferred.await() val dataToSign = dataToSignDeferred.await()
.getOrElse { .getOrElse { error ->
otpTaskDeferred.cancel() otpTaskDeferred.cancel()
return@coroutineScope CompletionResult.Failure(it) return@coroutineScope CompletionResult.Failure(error)
} }
otpTaskDeferred.await() otpTaskDeferred.await()

View file

@ -115,8 +115,8 @@ class VisaCustomerWalletApproveTask(
extendedPublicKey = extendedPublicKey, extendedPublicKey = extendedPublicKey,
) )
validationResult.onLeft { validationResult.onLeft { error ->
callback(CompletionResult.Failure(it.tangemError)) callback(CompletionResult.Failure(error.tangemError))
return return
} }
@ -137,8 +137,8 @@ class VisaCustomerWalletApproveTask(
val publicKey = findKeyWithoutDerivation( val publicKey = findKeyWithoutDerivation(
targetAddress = visaDataForApprove.targetAddress, targetAddress = visaDataForApprove.targetAddress,
card = CardDTO(card), card = CardDTO(card),
).getOrElse { ).getOrElse { error ->
callback(CompletionResult.Failure(it.tangemError)) callback(CompletionResult.Failure(error.tangemError))
return return
} }

View file

@ -37,7 +37,7 @@ class CreateSecondTwinWalletTask(
} }
if (!TwinsHelper.isTwinsCompatible(firstCardId, card.cardId)) { if (!TwinsHelper.isTwinsCompatible(firstCardId, card.cardId)) {
callback(CompletionResult.Failure(IncompatibleTwinCard)) callback(CompletionResult.Failure(IncompatibleTwinCard()))
return return
} }

View file

@ -24,7 +24,7 @@ class FinalizeTwinTask(
when (readResult) { when (readResult) {
is CompletionResult.Success -> is CompletionResult.Success ->
ScanProductTask( ScanProductTask(
readResult.data, card = readResult.data,
derivationsFinder = null, derivationsFinder = null,
visaCardScanHandler = null, visaCardScanHandler = null,
visaCoroutineScope = null, visaCoroutineScope = null,

View file

@ -4,7 +4,7 @@ import com.tangem.common.core.TangemError
import com.tangem.tap.tangemSdkManager import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R import com.tangem.wallet.R
object IncompatibleTwinCard : TangemError(code = 50005) { class IncompatibleTwinCard : TangemError(code = 50005) {
override var customMessage: String = tangemSdkManager.getString( override var customMessage: String = tangemSdkManager.getString(
R.string.twin_error_wrong_twin, R.string.twin_error_wrong_twin,
) )

View file

@ -41,7 +41,7 @@ class TwinCardsManager(card: CardDTO) {
creatingWalletMessage: Message, creatingWalletMessage: Message,
): CompletionResult<CreateWalletResponse> { ): CompletionResult<CreateWalletResponse> {
val response = tangemSdkManager.createSecondTwinWallet( val response = tangemSdkManager.createSecondTwinWallet(
firstPublicKey = currentCardPublicKey!!, firstPublicKey = requireNotNull(currentCardPublicKey),
firstCardId = firstCardId, firstCardId = firstCardId,
issuerKeys = getIssuerKeys(), issuerKeys = getIssuerKeys(),
preparingMessage = preparingMessage, preparingMessage = preparingMessage,
@ -58,7 +58,7 @@ class TwinCardsManager(card: CardDTO) {
suspend fun complete(message: Message): CompletionResult<ScanResponse> { suspend fun complete(message: Message): CompletionResult<ScanResponse> {
val response = tangemSdkManager.finalizeTwin( val response = tangemSdkManager.finalizeTwin(
secondCardPublicKey = secondCardPublicKey!!.hexToBytes(), secondCardPublicKey = requireNotNull(secondCardPublicKey).hexToBytes(),
issuerKeyPair = getIssuerKeys(), issuerKeyPair = getIssuerKeys(),
cardId = firstCardId, cardId = firstCardId,
initialMessage = message, initialMessage = message,

View file

@ -21,8 +21,9 @@ class WriteProtectedIssuerDataTask(
override fun run(session: CardSession, callback: (result: CompletionResult<SuccessResponse>) -> Unit) { override fun run(session: CardSession, callback: (result: CompletionResult<SuccessResponse>) -> Unit) {
SignHashCommand( SignHashCommand(
twinPublicKey.calculateSha256(), hash = twinPublicKey.calculateSha256(),
session.environment.card!!.wallets.first().publicKey, walletPublicKey = requireNotNull(session.environment.card)
.wallets.first().publicKey,
) )
.run(session) { signResult -> .run(session) { signResult ->
when (signResult) { when (signResult) {
@ -31,12 +32,12 @@ class WriteProtectedIssuerDataTask(
when (readResult) { when (readResult) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
writeIssuerData( writeIssuerData(
twinPublicKey, twinPublicKey = twinPublicKey,
issuerKeys, issuerKeys = issuerKeys,
signResult.data.signature, cardSignature = signResult.data.signature,
readResult.data, readResponse = readResult.data,
session, session = session,
callback, callback = callback,
) )
} }
is CompletionResult.Failure -> callback( is CompletionResult.Failure -> callback(
@ -78,7 +79,7 @@ class WriteProtectedIssuerDataTask(
) )
WriteIssuerDataCommand( WriteIssuerDataCommand(
issuerData = data, issuerData = data,
issuerDataSignature = signedByIssuer.finalizingSignature!!, issuerDataSignature = requireNotNull(signedByIssuer.finalizingSignature),
issuerDataCounter = counter, issuerDataCounter = counter,
issuerPublicKey = issuerKeys.publicKey, issuerPublicKey = issuerKeys.publicKey,
).run(session, callback) ).run(session, callback)

View file

@ -57,12 +57,12 @@ internal class BiometricUserWalletsListManager(
override val selectedUserWalletSync: UserWallet? override val selectedUserWalletSync: UserWallet?
get() = findSelectedUserWallet() get() = findSelectedUserWallet()
override val isLocked: Flow<Boolean> override val lockedState: Flow<Boolean>
get() = state get() = state
.mapLatest { it.isLocked } .mapLatest { it.isLocked }
.distinctUntilChanged() .distinctUntilChanged()
override val isLockedSync: Boolean override val isLocked: Boolean
get() = state.value.isLocked get() = state.value.isLocked
override val hasUserWallets: Boolean override val hasUserWallets: Boolean
@ -103,7 +103,9 @@ internal class BiometricUserWalletsListManager(
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching { override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
if (state.value.selectedUserWalletId == userWalletId) { if (state.value.selectedUserWalletId == userWalletId) {
return@catching findSelectedUserWallet()!! return@catching requireNotNull(findSelectedUserWallet()) {
"Wallet is not found"
}
} }
selectedUserWalletRepository.set(userWalletId) selectedUserWalletRepository.set(userWalletId)

View file

@ -93,23 +93,23 @@ internal class GeneralUserWalletsListManager(
override val walletsCount: Int override val walletsCount: Int
get() = requireImplementation.walletsCount get() = requireImplementation.walletsCount
override val isLocked: Flow<Boolean> override val lockedState: Flow<Boolean>
get() = implementation.transformLatest { impl -> get() = implementation.transformLatest { impl ->
if (impl == null) return@transformLatest if (impl == null) return@transformLatest
if (impl is UserWalletsListManager.Lockable) { if (impl is UserWalletsListManager.Lockable) {
emitAll(impl.isLocked) emitAll(impl.lockedState)
} else { } else {
error("RuntimeUserWalletsListManager is not lockable") error("RuntimeUserWalletsListManager is not lockable")
} }
} }
override val isLockedSync: Boolean override val isLocked: Boolean
get() { get() {
val impl = requireImplementation val impl = requireImplementation
return if (impl is UserWalletsListManager.Lockable) { return if (impl is UserWalletsListManager.Lockable) {
impl.isLockedSync impl.isLocked
} else { } else {
error("RuntimeUserWalletsListManager is not lockable") error("RuntimeUserWalletsListManager is not lockable")
} }
@ -173,10 +173,10 @@ internal class GeneralUserWalletsListManager(
} }
if (possibleManager == implementation.value) { if (possibleManager == implementation.value) {
Timber.e("Switch to the same manager ${possibleManager::class.simpleName}") Timber.e("Switch to the same manager ${possibleManager::class.simpleName.orEmpty()}")
} }
Timber.i("Switch to ${possibleManager::class.simpleName}") Timber.i("Switch to ${possibleManager::class.simpleName.orEmpty()}")
val previousManager = implementation.value val previousManager = implementation.value
implementation.value = copySelectedUserWallet( implementation.value = copySelectedUserWallet(

View file

@ -74,11 +74,13 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
?.takeIf { it.walletId == userWalletId } ?.takeIf { it.walletId == userWalletId }
?: walletNotFound() ?: walletNotFound()
state.updateAndGet { prevState -> requireNotNull(
prevState.copy( state.updateAndGet { prevState ->
userWallet = update(wallet), prevState.copy(
) userWallet = update(wallet),
}.userWallet!! )
}.userWallet,
) { "User wallet is null after update" }
} }
override suspend fun delete(userWalletIds: List<UserWalletId>): CompletionResult<Unit> = clear() override suspend fun delete(userWalletIds: List<UserWalletId>): CompletionResult<Unit> = clear()

View file

@ -21,6 +21,7 @@ internal data class UserWalletSensitiveInformation(
val mobileWallets: List<MobileWallet>? = null, val mobileWallets: List<MobileWallet>? = null,
) )
@Suppress("BooleanPropertyNaming")
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class UserWalletPublicInformation( internal data class UserWalletPublicInformation(
// Common // Common

View file

@ -125,7 +125,7 @@ internal class DefaultUserWalletsListRepository(
// update the userWallets state and add if it doesn't exist // update the userWallets state and add if it doesn't exist
updateWallets { currentWallets -> updateWallets { currentWallets ->
val wallets = currentWallets ?: emptyList() val wallets = currentWallets.orEmpty()
if (wallets.any { it.walletId == userWallet.walletId }) { if (wallets.any { it.walletId == userWallet.walletId }) {
wallets.map { if (it.walletId == userWallet.walletId) userWallet else it } wallets.map { if (it.walletId == userWallet.walletId) userWallet else it }
} else { } else {
@ -332,12 +332,12 @@ internal class DefaultUserWalletsListRepository(
raise(LockWalletsError.NothingToLock) raise(LockWalletsError.NothingToLock)
} }
updateWallets { updateWallets { wallets ->
it?.map { wallets?.map { wallet ->
if (it.walletId !in unsecuredWalletIds) { if (wallet.walletId !in unsecuredWalletIds) {
it.lock() wallet.lock()
} else { } else {
it wallet
} }
} }
} }
@ -395,17 +395,17 @@ internal class DefaultUserWalletsListRepository(
} }
private suspend fun hasBiometry(): Boolean { private suspend fun hasBiometry(): Boolean {
val useBiometricAuthentication = appPreferencesStore.getSyncOrDefault( val isBiometricAuthenticationUsed = appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.USE_BIOMETRIC_AUTHENTICATION_KEY, key = PreferencesKeys.USE_BIOMETRIC_AUTHENTICATION_KEY,
default = false, default = false,
) )
return tangemSdkManagerProvider.invoke().canUseBiometry && useBiometricAuthentication return tangemSdkManagerProvider.invoke().canUseBiometry && isBiometricAuthenticationUsed
} }
private fun updateWallets(block: (List<UserWallet>?) -> List<UserWallet>?) { private fun updateWallets(block: (List<UserWallet>?) -> List<UserWallet>?) {
userWallets.update { userWallets.update { wallets ->
val updated = block(it) val updated = block(wallets)
selectedUserWallet.update { currentSelected -> selectedUserWallet.update { currentSelected ->
if (currentSelected == null) return@update null if (currentSelected == null) return@update null
updated?.find { it.walletId == currentSelected.walletId } updated?.find { it.walletId == currentSelected.walletId }

View file

@ -137,8 +137,7 @@ internal class UserWalletEncryptionKeysRepository(
private suspend fun UserWalletEncryptionKey.encode(): ByteArray { private suspend fun UserWalletEncryptionKey.encode(): ByteArray {
return withContext(dispatchers.default) { return withContext(dispatchers.default) {
this@encode encryptionKeyAdapter.toJson(this@encode)
.let(encryptionKeyAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }
@ -153,8 +152,7 @@ internal class UserWalletEncryptionKeysRepository(
private suspend fun List<UserWalletId>.encode(): ByteArray { private suspend fun List<UserWalletId>.encode(): ByteArray {
return withContext(dispatchers.default) { return withContext(dispatchers.default) {
this@encode userWalletsIdsListAdapter.toJson(this@encode)
.let(userWalletsIdsListAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }

View file

@ -164,8 +164,7 @@ internal class BiometricUserWalletsKeysRepository(
private suspend fun UserWalletEncryptionKey.encode(): ByteArray { private suspend fun UserWalletEncryptionKey.encode(): ByteArray {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
this@encode encryptionKeyAdapter.toJson(this@encode)
.let(encryptionKeyAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }
@ -180,8 +179,7 @@ internal class BiometricUserWalletsKeysRepository(
private suspend fun List<UserWalletId>.encode(): ByteArray { private suspend fun List<UserWalletId>.encode(): ByteArray {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
this@encode userWalletsIdsListAdapter.toJson(this@encode)
.let(userWalletsIdsListAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }

View file

@ -80,8 +80,7 @@ internal class DefaultUserWalletsPublicInformationRepository(
@JvmName("saveWithPublicInformation") @JvmName("saveWithPublicInformation")
private suspend fun save(publicInformation: List<UserWalletPublicInformation>): CompletionResult<Unit> = catching { private suspend fun save(publicInformation: List<UserWalletPublicInformation>): CompletionResult<Unit> = catching {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
publicInformation publicInformationAdapter.toJson(publicInformation)
.let(publicInformationAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
.also { secureStorage.store(it, StorageKey.UserWalletPublicInformation.name) } .also { secureStorage.store(it, StorageKey.UserWalletPublicInformation.name) }
} }

View file

@ -110,18 +110,18 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
private suspend fun ByteArray.decodeToEncryptedSensitiveInformation(): Map<String, ByteArray>? { private suspend fun ByteArray.decodeToEncryptedSensitiveInformation(): Map<String, ByteArray>? {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
this@decodeToEncryptedSensitiveInformation encryptedSensitiveInformationMapAdapter.fromJson(
.decodeToString(throwOnInvalidSequence = true) this@decodeToEncryptedSensitiveInformation.decodeToString(throwOnInvalidSequence = true),
.let(encryptedSensitiveInformationMapAdapter::fromJson) )
} }
} }
private suspend fun ByteArray.decodeToSensitiveInformation(): UserWalletSensitiveInformation? { private suspend fun ByteArray.decodeToSensitiveInformation(): UserWalletSensitiveInformation? {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
try { try {
this@decodeToSensitiveInformation sensitiveInformationAdapter.fromJson(
.decodeToString(throwOnInvalidSequence = true) this@decodeToSensitiveInformation.decodeToString(throwOnInvalidSequence = true),
.let(sensitiveInformationAdapter::fromJson) )
} catch (e: CharacterCodingException) { } catch (e: CharacterCodingException) {
Timber.e(e, "Unable to decode sensitive information") Timber.e(e, "Unable to decode sensitive information")
@ -132,16 +132,14 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
private suspend fun UserWalletSensitiveInformation.encode(): ByteArray { private suspend fun UserWalletSensitiveInformation.encode(): ByteArray {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
this@encode sensitiveInformationAdapter.toJson(this@encode)
.let(sensitiveInformationAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }
private suspend fun Map<String, ByteArray>.encode(): ByteArray { private suspend fun Map<String, ByteArray>.encode(): ByteArray {
return withContext(Dispatchers.Default) { return withContext(Dispatchers.Default) {
this@encode encryptedSensitiveInformationMapAdapter.toJson(this@encode)
.let(encryptedSensitiveInformationMapAdapter::toJson)
.encodeToByteArray(throwOnInvalidSequence = true) .encodeToByteArray(throwOnInvalidSequence = true)
} }
} }

View file

@ -54,14 +54,14 @@ internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
walletId = walletId, walletId = walletId,
hotWalletId = hotWalletId, hotWalletId = hotWalletId,
wallets = null, wallets = null,
backedUp = backedUp!!, backedUp = requireNotNull(backedUp),
) )
} else { } else {
UserWallet.Cold( UserWallet.Cold(
name = name, name = name,
walletId = walletId, walletId = walletId,
cardsInWallet = cardsInWallet, cardsInWallet = cardsInWallet,
scanResponse = scanResponse!!, scanResponse = requireNotNull(scanResponse),
isMultiCurrency = isMultiCurrency, isMultiCurrency = isMultiCurrency,
hasBackupError = hasBackupError, hasBackupError = hasBackupError,
) )
@ -78,7 +78,7 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
copy( copy(
scanResponse = scanResponse.copy( scanResponse = scanResponse.copy(
card = scanResponse.card.copy( card = scanResponse.card.copy(
wallets = sensitiveInformation.wallets!!, wallets = requireNotNull(sensitiveInformation.wallets),
), ),
visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus, visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
), ),

View file

@ -87,9 +87,9 @@ internal class VisaCardScanHandler @Inject constructor(
cardId = card.cardId, cardId = card.cardId,
// This is the wallet public key, not the address and it's alright, as the API expects it in this format // This is the wallet public key, not the address and it's alright, as the API expects it in this format
cardWalletAddress = wallet.publicKey.toHexString(), cardWalletAddress = wallet.publicKey.toHexString(),
).getOrElse { ).getOrElse { error ->
Timber.i("Failed to get Access token for Wallet public key authorization") Timber.i("Failed to get Access token for Wallet public key authorization")
return CompletionResult.Failure(it.tangemError) return CompletionResult.Failure(error.tangemError)
} }
val signChallengeResult = signChallengeWithWallet( val signChallengeResult = signChallengeWithWallet(
@ -123,16 +123,16 @@ internal class VisaCardScanHandler @Inject constructor(
signedChallenge: VisaAuthSignedChallenge, signedChallenge: VisaAuthSignedChallenge,
): CompletionResult<VisaCardActivationStatus> { ): CompletionResult<VisaCardActivationStatus> {
val authorizationTokensResponse = visaAuthRemoteDataSource.getAccessTokens(signedChallenge = signedChallenge) val authorizationTokensResponse = visaAuthRemoteDataSource.getAccessTokens(signedChallenge = signedChallenge)
.getOrElse { .getOrElse { error ->
Timber.i("Failed to get Access token for Wallet public key authorization.") Timber.i("Failed to get Access token for Wallet public key authorization.")
return if ( return if (
it is VisaApiError.ProductInstanceIsNotActivated || error is VisaApiError.ProductInstanceIsNotActivated ||
it is VisaApiError.ProductInstanceNotFoundActivationRequired error is VisaApiError.ProductInstanceNotFoundActivationRequired
) { ) {
Timber.i("Proceeding with card authorization.") Timber.i("Proceeding with card authorization.")
handleCardAuthorization(cardWalletAddress = cardWalletAddress) handleCardAuthorization(cardWalletAddress = cardWalletAddress)
} else { } else {
CompletionResult.Failure(it.tangemError) CompletionResult.Failure(error.tangemError)
} }
} }
@ -152,9 +152,9 @@ internal class VisaCardScanHandler @Inject constructor(
val challengeResponse = visaAuthRemoteDataSource.getCardAuthChallenge( val challengeResponse = visaAuthRemoteDataSource.getCardAuthChallenge(
cardId = card.cardId, cardId = card.cardId,
cardPublicKey = card.cardPublicKey.toHexString(), cardPublicKey = card.cardPublicKey.toHexString(),
).getOrElse { ).getOrElse { error ->
Timber.e("Failed to get challenge for Card authorization. Plain error: ${it.errorCode}") Timber.e("Failed to get challenge for Card authorization. Plain error: ${error.errorCode}")
return CompletionResult.Failure(it.tangemError) return CompletionResult.Failure(error.tangemError)
} }
Timber.i("Received challenge to sign: ${challengeResponse.challenge}") Timber.i("Received challenge to sign: ${challengeResponse.challenge}")
@ -179,9 +179,9 @@ internal class VisaCardScanHandler @Inject constructor(
signedChallenge = attestCardKeyResponse.cardSignature.toHexString(), signedChallenge = attestCardKeyResponse.cardSignature.toHexString(),
salt = attestCardKeyResponse.salt.toHexString(), salt = attestCardKeyResponse.salt.toHexString(),
), ),
).getOrElse { ).getOrElse { error ->
Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.errorCode}") Timber.e("Failed to sign challenge with Card public key. Plain error: ${error.errorCode}")
return CompletionResult.Failure(it.tangemError) return CompletionResult.Failure(error.tangemError)
} }
visaAuthTokenStorage.store( visaAuthTokenStorage.store(
@ -189,9 +189,9 @@ internal class VisaCardScanHandler @Inject constructor(
tokens = authorizationTokensResponse, tokens = authorizationTokensResponse,
) )
val activationRemoteState = visaActivationRepository.getActivationRemoteState().getOrElse { val activationRemoteState = visaActivationRepository.getActivationRemoteState().getOrElse { error ->
Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.errorCode}") Timber.e("Failed to sign challenge with Card public key. Plain error: ${error.errorCode}")
return CompletionResult.Failure(it.tangemError) return CompletionResult.Failure(error.tangemError)
} }
val error = when (activationRemoteState) { val error = when (activationRemoteState) {

View file

@ -6,6 +6,7 @@ import com.tangem.domain.models.scan.ScanResponse
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import org.rekotlin.Action import org.rekotlin.Action
@Suppress("BooleanPropertyNaming")
sealed class DetailsAction : Action { sealed class DetailsAction : Action {
data class PrepareScreen( data class PrepareScreen(
@ -32,7 +33,7 @@ sealed class DetailsAction : Action {
data object EnrollBiometrics : AppSettings() data object EnrollBiometrics : AppSettings()
data class BiometricsStatusChanged( data class BiometricsStatusChanged(
val needEnrollBiometrics: Boolean, val isEnrollBiometricsNeeded: Boolean,
) : AppSettings() ) : AppSettings()
data class ChangeAppThemeMode( data class ChangeAppThemeMode(
@ -40,7 +41,7 @@ sealed class DetailsAction : Action {
) : AppSettings() ) : AppSettings()
data class ChangeBalanceHiding( data class ChangeBalanceHiding(
val hideBalance: Boolean, val shouldHideBalance: Boolean,
) : AppSettings() ) : AppSettings()
data class ChangeAppCurrency( data class ChangeAppCurrency(

View file

@ -86,7 +86,7 @@ class DetailsMiddleware {
changeAppThemeMode(action.appThemeMode) changeAppThemeMode(action.appThemeMode)
} }
is DetailsAction.AppSettings.ChangeBalanceHiding -> { is DetailsAction.AppSettings.ChangeBalanceHiding -> {
changeBalanceHiding(action.hideBalance) changeBalanceHiding(action.shouldHideBalance)
} }
is DetailsAction.AppSettings.ChangeAppCurrency -> { is DetailsAction.AppSettings.ChangeAppCurrency -> {
store.dispatch(GlobalAction.ChangeAppCurrency(action.currency)) store.dispatch(GlobalAction.ChangeAppCurrency(action.currency))
@ -153,9 +153,9 @@ class DetailsMiddleware {
private suspend fun setBiometricLockForAllWallets() { private suspend fun setBiometricLockForAllWallets() {
val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository) val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository)
val userWallets = userWalletsListRepository.userWalletsSync() val userWallets = userWalletsListRepository.userWalletsSync()
userWallets.forEach { userWallets.forEach { wallet ->
userWalletsListRepository.setLock( userWalletsListRepository.setLock(
userWalletId = it.walletId, userWalletId = wallet.walletId,
lockMethod = LockMethod.Biometric, lockMethod = LockMethod.Biometric,
changeUnsecured = false, changeUnsecured = false,
) )
@ -174,11 +174,11 @@ class DetailsMiddleware {
deleteSavedAccessCodes() deleteSavedAccessCodes()
val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository) val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository)
val tangemHotSdk = store.inject(DaggerGraphState::tangemHotSdk) val tangemHotSdk = store.inject(DaggerGraphState::tangemHotSdk)
userWalletsListRepository.userWalletsSync().forEach { userWalletsListRepository.userWalletsSync().forEach { wallet ->
if (it is UserWallet.Hot) { if (wallet is UserWallet.Hot) {
userWalletsListRepository.saveWithoutLock( userWalletsListRepository.saveWithoutLock(
userWallet = it.copy( userWallet = wallet.copy(
hotWalletId = tangemHotSdk.removeBiometryAuthIfPresented(it.hotWalletId), hotWalletId = tangemHotSdk.removeBiometryAuthIfPresented(wallet.hotWalletId),
), ),
) )
} }
@ -188,10 +188,10 @@ class DetailsMiddleware {
private fun observeBiometricsStatusChanges(scope: CoroutineScope) { private fun observeBiometricsStatusChanges(scope: CoroutineScope) {
val needEnrollBiometricsFlow = flow { val needEnrollBiometricsFlow = flow {
do { do {
val needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() val isEnrollBiometricsNeeded = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull()
if (needEnrollBiometrics != null) { if (isEnrollBiometricsNeeded != null) {
emit(needEnrollBiometrics) emit(isEnrollBiometricsNeeded)
} }
delay(timeMillis = 200) delay(timeMillis = 200)

View file

@ -84,7 +84,7 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail
) )
is DetailsAction.AppSettings.BiometricsStatusChanged -> state.copy( is DetailsAction.AppSettings.BiometricsStatusChanged -> state.copy(
appSettingsState = state.appSettingsState.copy( appSettingsState = state.appSettingsState.copy(
needEnrollBiometrics = action.needEnrollBiometrics, needEnrollBiometrics = action.isEnrollBiometricsNeeded,
), ),
) )
is DetailsAction.AppSettings.ChangeAppThemeMode -> state.copy( is DetailsAction.AppSettings.ChangeAppThemeMode -> state.copy(
@ -99,7 +99,7 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail
) )
is DetailsAction.AppSettings.ChangeBalanceHiding -> state.copy( is DetailsAction.AppSettings.ChangeBalanceHiding -> state.copy(
appSettingsState = state.appSettingsState.copy( appSettingsState = state.appSettingsState.copy(
isHidingEnabled = action.hideBalance, isHidingEnabled = action.shouldHideBalance,
), ),
) )
// state should be copied to avoid concurrent modifications from different sources // state should be copied to avoid concurrent modifications from different sources

View file

@ -11,6 +11,7 @@ data class DetailsState(
val appSettingsState: AppSettingsState = AppSettingsState(), val appSettingsState: AppSettingsState = AppSettingsState(),
) : StateType ) : StateType
@Suppress("BooleanPropertyNaming")
data class AppSettingsState( data class AppSettingsState(
@Deprecated("Delete after hot wallet release") @Deprecated("Delete after hot wallet release")
val saveWallets: Boolean = false, val saveWallets: Boolean = false,

View file

@ -341,16 +341,16 @@ private class AppCurrencySelectorStateProvider : CollectionPreviewParameterProvi
.mapIndexed { index, s -> Currency(index.toString(), s) } .mapIndexed { index, s -> Currency(index.toString(), s) }
.toPersistentList() .toPersistentList()
AppCurrencySelectorState.Loading(onBackClick = {}).let(::add) add(AppCurrencySelectorState.Loading(onBackClick = {}))
AppCurrencySelectorState.Default( add(AppCurrencySelectorState.Default(
selectedId = "0", selectedId = "0",
items = items, items = items,
scrollToSelected = consumedEvent(), scrollToSelected = consumedEvent(),
onCurrencyClick = {}, onCurrencyClick = {},
onBackClick = {}, onBackClick = {},
onTopBarActionClick = {}, onTopBarActionClick = {},
).let(::add) ))
AppCurrencySelectorState.Search( add(AppCurrencySelectorState.Search(
selectedId = "0", selectedId = "0",
items = items, items = items,
scrollToSelected = consumedEvent(), scrollToSelected = consumedEvent(),
@ -358,7 +358,7 @@ private class AppCurrencySelectorStateProvider : CollectionPreviewParameterProvi
onBackClick = {}, onBackClick = {},
onSearchInputChange = {}, onSearchInputChange = {},
onTopBarActionClick = {}, onTopBarActionClick = {},
).let(::add) ))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -27,16 +27,16 @@ import kotlinx.collections.immutable.persistentListOf
@Composable @Composable
internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) {
SettingsScreensScaffold( SettingsScreensScaffold(
onBackClick = onBackClick,
modifier = modifier, modifier = modifier,
titleRes = R.string.app_settings_title,
addBottomInsets = false,
content = { content = {
when (state) { when (state) {
is AppSettingsScreenState.Content -> AppSettings(state = state) is AppSettingsScreenState.Content -> AppSettings(state = state)
is AppSettingsScreenState.Loading -> Unit is AppSettingsScreenState.Loading -> Unit
} }
}, },
titleRes = R.string.app_settings_title,
onBackClick = onBackClick,
addBottomInsets = false,
) )
} }
@ -103,10 +103,10 @@ private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvide
itemsFactory.createSelectThemeModeButton(AppThemeMode.DEFAULT, {}), itemsFactory.createSelectThemeModeButton(AppThemeMode.DEFAULT, {}),
) )
AppSettingsScreenState.Content( add(AppSettingsScreenState.Content(
items = items, items = items,
dialog = null, dialog = null,
).let(::add) ))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -47,8 +47,8 @@ private class AlertDialogProvider : CollectionPreviewParameterProvider<Dialog.Al
collection = buildList { collection = buildList {
val dialogsFactory = AppSettingsDialogsFactory() val dialogsFactory = AppSettingsDialogsFactory()
dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}).let(::add) add(dialogsFactory.createDeleteSavedAccessCodesAlert({}, {}))
dialogsFactory.createDeleteSavedWalletsAlert({}, {}).let(::add) add(dialogsFactory.createDeleteSavedWalletsAlert({}, {}))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -64,10 +64,10 @@ private class ButtonItemProvider : CollectionPreviewParameterProvider<Item.Butto
collection = buildList { collection = buildList {
val itemsFactory = AppSettingsItemsFactory() val itemsFactory = AppSettingsItemsFactory()
itemsFactory.createSelectAppCurrencyButton( add(itemsFactory.createSelectAppCurrencyButton(
currentAppCurrencyName = "US Dollar", currentAppCurrencyName = "US Dollar",
onClick = { /* no-op */ }, onClick = { /* no-op */ },
).let(::add) ))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -71,9 +71,9 @@ private class CardItemProvider : CollectionPreviewParameterProvider<Item.Card>(
collection = buildList { collection = buildList {
val itemsFactory = AppSettingsItemsFactory() val itemsFactory = AppSettingsItemsFactory()
itemsFactory.createEnrollBiometricsCard( add(itemsFactory.createEnrollBiometricsCard(
onClick = { /* no-op */ }, onClick = { /* no-op */ },
).let(::add) ))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -85,26 +85,26 @@ private class SwitchItemProvider : CollectionPreviewParameterProvider<Item.Switc
collection = buildList { collection = buildList {
val itemsFactory = AppSettingsItemsFactory() val itemsFactory = AppSettingsItemsFactory()
itemsFactory.createSaveAccessCodeSwitch( add(itemsFactory.createSaveAccessCodeSwitch(
isChecked = true, isChecked = true,
isEnabled = true, isEnabled = true,
onCheckedChange = { /* no-op */ }, onCheckedChange = { /* no-op */ },
).let(::add) ))
itemsFactory.createSaveAccessCodeSwitch( add(itemsFactory.createSaveAccessCodeSwitch(
isChecked = false, isChecked = false,
isEnabled = true, isEnabled = true,
onCheckedChange = { /* no-op */ }, onCheckedChange = { /* no-op */ },
).let(::add) ))
itemsFactory.createSaveAccessCodeSwitch( add(itemsFactory.createSaveAccessCodeSwitch(
isChecked = true, isChecked = true,
isEnabled = false, isEnabled = false,
onCheckedChange = { /* no-op */ }, onCheckedChange = { /* no-op */ },
).let(::add) ))
itemsFactory.createSaveAccessCodeSwitch( add(itemsFactory.createSaveAccessCodeSwitch(
isChecked = false, isChecked = false,
isEnabled = false, isEnabled = false,
onCheckedChange = { /* no-op */ }, onCheckedChange = { /* no-op */ },
).let(::add) ))
}, },
) )
// endregion Preview // endregion Preview

View file

@ -99,60 +99,76 @@ internal class AppSettingsModel @Inject constructor(
} }
private fun buildItems(state: AppSettingsState): ImmutableList<AppSettingsScreenState.Item> { private fun buildItems(state: AppSettingsState): ImmutableList<AppSettingsScreenState.Item> {
val items = buildList { val items = buildList<AppSettingsScreenState.Item> {
if (state.needEnrollBiometrics) { if (state.needEnrollBiometrics) {
itemsFactory.createEnrollBiometricsCard( add(
onClick = ::enrollBiometrics, itemsFactory.createEnrollBiometricsCard(
).let(::add) onClick = ::enrollBiometrics,
),
)
} }
itemsFactory.createSelectAppCurrencyButton( add(
currentAppCurrencyName = state.selectedAppCurrency.name, itemsFactory.createSelectAppCurrencyButton(
onClick = ::showAppCurrencySelector, currentAppCurrencyName = state.selectedAppCurrency.name,
).let(::add) onClick = ::showAppCurrencySelector,
),
)
if (hotWalletFeatureToggles.isHotWalletEnabled) { if (hotWalletFeatureToggles.isHotWalletEnabled) {
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
itemsFactory.createUseBiometricsSwitch( add(
isChecked = state.useBiometricAuthentication, itemsFactory.createUseBiometricsSwitch(
isEnabled = canUseBiometrics, isChecked = state.useBiometricAuthentication,
onCheckedChange = ::onBiometricAuthenticationToggled, isEnabled = canUseBiometrics,
).let(::add) onCheckedChange = ::onBiometricAuthenticationToggled,
),
)
itemsFactory.createRequireAccessCodeSwitch( add(
isChecked = state.requireAccessCode, itemsFactory.createRequireAccessCodeSwitch(
isEnabled = canUseBiometrics && state.useBiometricAuthentication, isChecked = state.requireAccessCode,
onCheckedChange = ::onRequireAccessCodeToggled, isEnabled = canUseBiometrics && state.useBiometricAuthentication,
).let(::add) onCheckedChange = ::onRequireAccessCodeToggled,
),
)
} else { } else {
if (state.isBiometricsAvailable) { if (state.isBiometricsAvailable) {
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
itemsFactory.createSaveWalletsSwitch( add(
isChecked = state.saveWallets, itemsFactory.createSaveWalletsSwitch(
isEnabled = canUseBiometrics, isChecked = state.saveWallets,
onCheckedChange = ::onSaveWalletsToggled, isEnabled = canUseBiometrics,
).let(::add) onCheckedChange = ::onSaveWalletsToggled,
),
)
itemsFactory.createSaveAccessCodeSwitch( add(
isChecked = state.saveAccessCodes, itemsFactory.createSaveAccessCodeSwitch(
isEnabled = canUseBiometrics, isChecked = state.saveAccessCodes,
onCheckedChange = ::onSaveAccessCodesToggled, isEnabled = canUseBiometrics,
).let(::add) onCheckedChange = ::onSaveAccessCodesToggled,
),
)
} }
} }
itemsFactory.createFlipToHideBalanceSwitch( add(
isChecked = state.isHidingEnabled, itemsFactory.createFlipToHideBalanceSwitch(
isEnabled = true, isChecked = state.isHidingEnabled,
onCheckedChange = ::onFlipToHideBalanceToggled, isEnabled = true,
).let(::add) onCheckedChange = ::onFlipToHideBalanceToggled,
),
)
itemsFactory.createSelectThemeModeButton( add(
currentThemeMode = state.selectedThemeMode, itemsFactory.createSelectThemeModeButton(
onClick = { showThemeModeSelector(state.selectedThemeMode) }, currentThemeMode = state.selectedThemeMode,
).let(::add) onClick = { showThemeModeSelector(state.selectedThemeMode) },
),
)
} }
return items.toImmutableList() return items.toImmutableList()
@ -280,7 +296,7 @@ internal class AppSettingsModel @Inject constructor(
val param = AnalyticsParam.OnOffState(enable) val param = AnalyticsParam.OnOffState(enable)
analyticsEventHandler.send(Settings.AppSettings.HideBalanceChanged(param)) analyticsEventHandler.send(Settings.AppSettings.HideBalanceChanged(param))
store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(hideBalance = enable)) store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(shouldHideBalance = enable))
} }
private fun dismissDialog() { private fun dismissDialog() {
@ -290,10 +306,10 @@ internal class AppSettingsModel @Inject constructor(
private fun bootstrapAppCurrencyUpdates() { private fun bootstrapAppCurrencyUpdates() {
appCurrencyRepository appCurrencyRepository
.getSelectedAppCurrency() .getSelectedAppCurrency()
.onEach { .onEach { appCurrency ->
if (it.code == store.state.globalState.appCurrency.code) return@onEach if (appCurrency.code == store.state.globalState.appCurrency.code) return@onEach
store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(it)) store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(appCurrency))
} }
.launchIn(scope) .launchIn(scope)
.saveIn(appCurrencyUpdatesJobHolder) .saveIn(appCurrencyUpdatesJobHolder)

View file

@ -26,19 +26,19 @@ import com.tangem.wallet.R
@Composable @Composable
internal fun CardSettingsScreen(state: CardSettingsScreenState, modifier: Modifier = Modifier) { internal fun CardSettingsScreen(state: CardSettingsScreenState, modifier: Modifier = Modifier) {
val needReadCard = state.cardDetails == null val isCardReadingNeeded = state.cardDetails == null
SettingsScreensScaffold( SettingsScreensScaffold(
onBackClick = state.onBackClick,
modifier = modifier, modifier = modifier,
titleRes = R.string.card_settings_title,
content = { content = {
if (needReadCard) { if (isCardReadingNeeded) {
CardSettingsReadCard(state.onScanCardClick) CardSettingsReadCard(state.onScanCardClick)
} else { } else {
CardSettings(state = state) CardSettings(state = state)
} }
}, },
titleRes = R.string.card_settings_title,
onBackClick = state.onBackClick,
) )
} }
@ -123,8 +123,8 @@ private fun CardSettings(state: CardSettingsScreenState) {
.fillMaxWidth() .fillMaxWidth()
.testTag(DeviceSettingsScreenTestTags.LAZY_LIST), .testTag(DeviceSettingsScreenTestTags.LAZY_LIST),
) { ) {
items(state.cardDetails) { items(state.cardDetails) { cardInfo ->
val paddingBottom = when (it) { val paddingBottom = when (cardInfo) {
is CardInfo.CardId, is CardInfo.Issuer -> TangemTheme.dimens.spacing12 is CardInfo.CardId, is CardInfo.Issuer -> TangemTheme.dimens.spacing12
is CardInfo.SignedHashes -> TangemTheme.dimens.spacing14 is CardInfo.SignedHashes -> TangemTheme.dimens.spacing14
is CardInfo.SecurityMode -> TangemTheme.dimens.spacing16 is CardInfo.SecurityMode -> TangemTheme.dimens.spacing16
@ -132,7 +132,7 @@ private fun CardSettings(state: CardSettingsScreenState) {
is CardInfo.AccessCodeRecovery -> TangemTheme.dimens.spacing16 is CardInfo.AccessCodeRecovery -> TangemTheme.dimens.spacing16
is CardInfo.ResetToFactorySettings -> TangemTheme.dimens.spacing28 is CardInfo.ResetToFactorySettings -> TangemTheme.dimens.spacing28
} }
val paddingTop = when (it) { val paddingTop = when (cardInfo) {
is CardInfo.CardId -> TangemTheme.dimens.spacing0 is CardInfo.CardId -> TangemTheme.dimens.spacing0
is CardInfo.Issuer -> TangemTheme.dimens.spacing12 is CardInfo.Issuer -> TangemTheme.dimens.spacing12
is CardInfo.SignedHashes -> TangemTheme.dimens.spacing12 is CardInfo.SignedHashes -> TangemTheme.dimens.spacing12
@ -145,8 +145,8 @@ private fun CardSettings(state: CardSettingsScreenState) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable( .clickable(
enabled = it.clickable, enabled = cardInfo.isClickable,
onClick = { state.onElementClick(it) }, onClick = { state.onElementClick(cardInfo) },
) )
.padding( .padding(
start = TangemTheme.dimens.spacing20, start = TangemTheme.dimens.spacing20,
@ -155,25 +155,25 @@ private fun CardSettings(state: CardSettingsScreenState) {
top = paddingTop, top = paddingTop,
), ),
) { ) {
val titleColor = if (it.clickable) { val titleColor = if (cardInfo.isClickable) {
TangemTheme.colors.text.primary1 TangemTheme.colors.text.primary1
} else { } else {
TangemTheme.colors.text.tertiary TangemTheme.colors.text.tertiary
} }
val subtitleColor = if (it.clickable) { val subtitleColor = if (cardInfo.isClickable) {
TangemTheme.colors.text.secondary TangemTheme.colors.text.secondary
} else { } else {
TangemTheme.colors.text.tertiary TangemTheme.colors.text.tertiary
} }
Text( Text(
text = it.titleRes.resolveReference(), text = cardInfo.titleRes.resolveReference(),
color = titleColor, color = titleColor,
style = TangemTheme.typography.subtitle1, style = TangemTheme.typography.subtitle1,
modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_TITLE), modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_TITLE),
) )
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4)) Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
Text( Text(
text = it.subtitle.resolveReference(), text = cardInfo.subtitle.resolveReference(),
color = subtitleColor, color = subtitleColor,
style = TangemTheme.typography.body2, style = TangemTheme.typography.body2,
modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_SUBTITLE), modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_SUBTITLE),

View file

@ -18,7 +18,7 @@ internal data class CardSettingsScreenState(
internal sealed class CardInfo( internal sealed class CardInfo(
val titleRes: TextReference, val titleRes: TextReference,
val subtitle: TextReference, val subtitle: TextReference,
val clickable: Boolean = false, val isClickable: Boolean = false,
) { ) {
class CardId(subtitle: String) : CardInfo( class CardId(subtitle: String) : CardInfo(
titleRes = TextReference.Res(R.string.details_row_title_cid), titleRes = TextReference.Res(R.string.details_row_title_cid),
@ -38,29 +38,29 @@ internal sealed class CardInfo(
class SecurityMode(securityOption: SecurityOption, clickable: Boolean) : CardInfo( class SecurityMode(securityOption: SecurityOption, clickable: Boolean) : CardInfo(
titleRes = TextReference.Res(R.string.card_settings_security_mode), titleRes = TextReference.Res(R.string.card_settings_security_mode),
subtitle = TextReference.Res(securityOption.toTitleRes()), subtitle = TextReference.Res(securityOption.toTitleRes()),
clickable = clickable, isClickable = clickable,
) )
data object ChangeAccessCode : CardInfo( data object ChangeAccessCode : CardInfo(
titleRes = TextReference.Res(R.string.card_settings_change_access_code), titleRes = TextReference.Res(R.string.card_settings_change_access_code),
subtitle = TextReference.Res(R.string.card_settings_change_access_code_footer), subtitle = TextReference.Res(R.string.card_settings_change_access_code_footer),
clickable = true, isClickable = true,
) )
class AccessCodeRecovery(val enabled: Boolean) : CardInfo( class AccessCodeRecovery(val isEnabled: Boolean) : CardInfo(
titleRes = TextReference.Res(R.string.card_settings_access_code_recovery_title), titleRes = TextReference.Res(R.string.card_settings_access_code_recovery_title),
subtitle = if (enabled) { subtitle = if (isEnabled) {
TextReference.Res(R.string.common_enabled) TextReference.Res(R.string.common_enabled)
} else { } else {
TextReference.Res(R.string.common_disabled) TextReference.Res(R.string.common_disabled)
}, },
clickable = true, isClickable = true,
) )
class ResetToFactorySettings(description: TextReference) : CardInfo( class ResetToFactorySettings(description: TextReference) : CardInfo(
titleRes = TextReference.Res(R.string.card_settings_reset_card_to_factory), titleRes = TextReference.Res(R.string.card_settings_reset_card_to_factory),
subtitle = description, subtitle = description,
clickable = true, isClickable = true,
) )
} }

View file

@ -16,8 +16,8 @@ import com.tangem.wallet.R
@Composable @Composable
fun AccessCodeRecoveryScreen(state: AccessCodeRecoveryScreenState, onBackClick: () -> Unit) { fun AccessCodeRecoveryScreen(state: AccessCodeRecoveryScreenState, onBackClick: () -> Unit) {
SettingsScreensScaffold( SettingsScreensScaffold(
content = { AccessCodeRecoveryOptions(state = state) },
onBackClick = onBackClick, onBackClick = onBackClick,
content = { AccessCodeRecoveryOptions(state = state) },
) )
} }
@ -38,13 +38,13 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) {
DetailsRadioButtonElement( DetailsRadioButtonElement(
title = stringResourceSafe(id = R.string.common_enabled), title = stringResourceSafe(id = R.string.common_enabled),
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_enabled_description), subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_enabled_description),
selected = state.enabledSelection, isSelected = state.isEnabledSelection,
onClick = { state.onOptionClick(true) }, onClick = { state.onOptionClick(true) },
) )
DetailsRadioButtonElement( DetailsRadioButtonElement(
title = stringResourceSafe(id = R.string.common_disabled), title = stringResourceSafe(id = R.string.common_disabled),
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_disabled_description), subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_disabled_description),
selected = !state.enabledSelection, isSelected = !state.isEnabledSelection,
onClick = { state.onOptionClick(false) }, onClick = { state.onOptionClick(false) },
) )

View file

@ -1,15 +1,15 @@
package com.tangem.tap.features.details.ui.cardsettings.coderecovery package com.tangem.tap.features.details.ui.cardsettings.coderecovery
/** /**
* @property enabledOnCard Indicates whether access code recovery is enabled on the card * @property isEnabledOnCard Indicates whether access code recovery is enabled on the card
* @property enabledSelection Represents the currently selected option in the app (not yet saved on the card) * @property isEnabledSelection Represents the currently selected option in the app (not yet saved on the card)
* @property isSaveChangesEnabled Determines if the user is allowed to save their selection to the card * @property isSaveChangesEnabled Determines if the user is allowed to save their selection to the card
* @property onSaveChangesClick Callback function called when the user wants to apply the selected option * @property onSaveChangesClick Callback function called when the user wants to apply the selected option
* @property onOptionClick Callback function called when the user selects an option * @property onOptionClick Callback function called when the user selects an option
* */ * */
data class AccessCodeRecoveryScreenState( data class AccessCodeRecoveryScreenState(
val enabledOnCard: Boolean, val isEnabledOnCard: Boolean,
val enabledSelection: Boolean, val isEnabledSelection: Boolean,
val isSaveChangesEnabled: Boolean, val isSaveChangesEnabled: Boolean,
val onSaveChangesClick: () -> Unit, val onSaveChangesClick: () -> Unit,
val onOptionClick: (Boolean) -> Unit, val onOptionClick: (Boolean) -> Unit,

View file

@ -43,8 +43,8 @@ internal class AccessCodeRecoveryModel @Inject constructor(
) )
return AccessCodeRecoveryScreenState( return AccessCodeRecoveryScreenState(
enabledOnCard = isEnabled, isEnabledOnCard = isEnabled,
enabledSelection = isEnabled, isEnabledSelection = isEnabled,
isSaveChangesEnabled = false, isSaveChangesEnabled = false,
onSaveChangesClick = ::saveChanges, onSaveChangesClick = ::saveChanges,
onOptionClick = ::selectOption, onOptionClick = ::selectOption,
@ -52,7 +52,7 @@ internal class AccessCodeRecoveryModel @Inject constructor(
} }
private fun saveChanges() = modelScope.launch { private fun saveChanges() = modelScope.launch {
val isEnabled = screenState.value.enabledSelection val isEnabled = screenState.value.isEnabledSelection
tangemSdkManager tangemSdkManager
.setAccessCodeRecoveryEnabled(scannedScanResponse.card.cardId, isEnabled) .setAccessCodeRecoveryEnabled(scannedScanResponse.card.cardId, isEnabled)
@ -78,10 +78,10 @@ internal class AccessCodeRecoveryModel @Inject constructor(
} }
private fun selectOption(isEnabled: Boolean) { private fun selectOption(isEnabled: Boolean) {
screenState.update { screenState.update { state ->
it.copy( state.copy(
enabledSelection = isEnabled, isEnabledSelection = isEnabled,
isSaveChangesEnabled = isEnabled != it.enabledOnCard, isSaveChangesEnabled = isEnabled != state.isEnabledOnCard,
) )
} }
} }

View file

@ -23,9 +23,9 @@ internal class CardSettingsInteractor @Inject constructor() {
} }
fun update(transform: (ScanResponse) -> ScanResponse) { fun update(transform: (ScanResponse) -> ScanResponse) {
_scannedScanResponse.update { _scannedScanResponse.update { scanResponse ->
requireNotNull(it) requireNotNull(scanResponse)
transform(it) transform(scanResponse)
} }
} }

View file

@ -57,7 +57,7 @@ internal class CardSettingsModel @Inject constructor(
private val params = paramsContainer.require<CardSettingsComponent.Params>() private val params = paramsContainer.require<CardSettingsComponent.Params>()
private var previousBiometricsRequestPolicy: Boolean = false private var isBiometricsRequestPolicyPrevious: Boolean = false
private val userWalletId = params.userWalletId private val userWalletId = params.userWalletId
@ -77,13 +77,13 @@ internal class CardSettingsModel @Inject constructor(
// Reset card scanned data // Reset card scanned data
cardSettingsInteractor.clear() cardSettingsInteractor.clear()
// Restore the previous value of access code request policy // Restore the previous value of access code request policy
cardSdkConfigRepository.isBiometricsRequestPolicy = previousBiometricsRequestPolicy cardSdkConfigRepository.isBiometricsRequestPolicy = isBiometricsRequestPolicyPrevious
} }
private fun updateAccessCodeRequestPolicy() { private fun updateAccessCodeRequestPolicy() {
runBlocking { runBlocking {
// !!!IMPORTANT!!!: Do not forget to restore the previous value in onCleared() method // !!!IMPORTANT!!!: Do not forget to restore the previous value in onCleared() method
previousBiometricsRequestPolicy = cardSdkConfigRepository.isBiometricsRequestPolicy isBiometricsRequestPolicyPrevious = cardSdkConfigRepository.isBiometricsRequestPolicy
val userWallet = getUserWalletUseCase(userWalletId) val userWallet = getUserWalletUseCase(userWalletId)
.getOrElse { error("User wallet $userWalletId not found") } .getOrElse { error("User wallet $userWalletId not found") }
@ -135,34 +135,38 @@ internal class CardSettingsModel @Inject constructor(
) )
val isResetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver) val isResetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver)
val cardDetails = buildList { val cardDetails = buildList<CardInfo> {
CardInfo.CardId(cardId).let(::add) add(CardInfo.CardId(cardId))
CardInfo.Issuer(card.issuer.name).let(::add) add(CardInfo.Issuer(card.issuer.name))
if (!cardTypesResolver.isTangemTwins()) { if (!cardTypesResolver.isTangemTwins()) {
CardInfo.SignedHashes(card.signedHashesCount().toString()).let(::add) add(CardInfo.SignedHashes(card.signedHashesCount().toString()))
} }
CardInfo.SecurityMode( add(
currentSecurityOption, CardInfo.SecurityMode(
clickable = allowedSecurityOptions.size > 1, currentSecurityOption,
).let(::add) clickable = allowedSecurityOptions.size > 1,
),
)
if (card.backupStatus?.isActive == true && card.isAccessCodeSet) { if (card.backupStatus?.isActive == true && card.isAccessCodeSet) {
CardInfo.ChangeAccessCode.let(::add) add(CardInfo.ChangeAccessCode)
} }
if (isAccessCodeRecoveryAllowed(cardTypesResolver)) { if (isAccessCodeRecoveryAllowed(cardTypesResolver)) {
CardInfo.AccessCodeRecovery(isAccessCodeRecoveryEnabled(cardTypesResolver, card)).let(::add) add(CardInfo.AccessCodeRecovery(isAccessCodeRecoveryEnabled(cardTypesResolver, card)))
} }
if (isResetCardAllowed) { if (isResetCardAllowed) {
CardInfo.ResetToFactorySettings( add(
description = getResetToFactoryDescription( CardInfo.ResetToFactorySettings(
isActiveBackupStatus = card.backupStatus?.isActive == true, description = getResetToFactoryDescription(
typesResolver = cardTypesResolver, isActiveBackupStatus = card.backupStatus?.isActive == true,
typesResolver = cardTypesResolver,
),
), ),
).let(::add) )
} }
} }

View file

@ -19,11 +19,11 @@ import com.tangem.wallet.R
@Composable @Composable
internal fun SettingsScreensScaffold( internal fun SettingsScreensScaffold(
onBackClick: () -> Unit, onBackClick: () -> Unit,
content: @Composable () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@StringRes titleRes: Int? = null, @StringRes titleRes: Int? = null,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
addBottomInsets: Boolean = true, addBottomInsets: Boolean = true,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
content: @Composable () -> Unit,
fab: @Composable () -> Unit = {}, fab: @Composable () -> Unit = {},
) { ) {
val backgroundColor = TangemTheme.colors.background.secondary val backgroundColor = TangemTheme.colors.background.secondary
@ -129,18 +129,18 @@ internal fun DetailsMainButton(
} }
@Composable @Composable
internal fun DetailsRadioButtonElement(title: String, subtitle: String, selected: Boolean, onClick: () -> Unit) { internal fun DetailsRadioButtonElement(title: String, subtitle: String, isSelected: Boolean, onClick: () -> Unit) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.selectable( .selectable(
selected = selected, selected = isSelected,
onClick = { onClick() }, onClick = { onClick() },
) )
.padding(start = 20.dp, end = 20.dp, top = 16.dp, bottom = 16.dp), .padding(start = 20.dp, end = 20.dp, top = 16.dp, bottom = 16.dp),
) { ) {
RadioButton( RadioButton(
selected = selected, selected = isSelected,
onClick = null, onClick = null,
modifier = Modifier.padding(end = 20.dp), modifier = Modifier.padding(end = 20.dp),
colors = RadioButtonDefaults.colors( colors = RadioButtonDefaults.colors(

View file

@ -7,7 +7,7 @@ internal fun isAccessCodeRecoveryAllowed(typeResolver: CardTypesResolver): Boole
internal fun isAccessCodeRecoveryEnabled(typeResolver: CardTypesResolver, card: CardDTO): Boolean = internal fun isAccessCodeRecoveryEnabled(typeResolver: CardTypesResolver, card: CardDTO): Boolean =
if (typeResolver.isWallet2()) { if (typeResolver.isWallet2()) {
card.userSettings?.isUserCodeRecoveryAllowed ?: false card.userSettings?.isUserCodeRecoveryAllowed == true
} else { } else {
false false
} }

View file

@ -27,11 +27,11 @@ import com.tangem.tap.features.details.ui.resetcard.ResetCardScreenState.Dialog
@Composable @Composable
internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) {
SettingsScreensScaffold( SettingsScreensScaffold(
onBackClick = onBackClick,
modifier = modifier, modifier = modifier,
content = { content = {
ResetCardView(state = state) ResetCardView(state = state)
}, },
onBackClick = onBackClick,
) )
when (val dialog = state.dialog) { when (val dialog = state.dialog) {
@ -65,7 +65,7 @@ private fun ResetCardView(state: ResetCardScreenState) {
Conditions(state) Conditions(state)
DynamicSpacer(scrollState = scrollState) DynamicSpacer(scrollState = scrollState)
SpacerH16() SpacerH16()
ResetButton(enabled = state.resetButtonEnabled, onResetButtonClick = state.onResetButtonClick) ResetButton(enabled = state.isResetButtonEnabled, onResetButtonClick = state.onResetButtonClick)
SpacerH16() SpacerH16()
} }
} }
@ -113,18 +113,18 @@ private fun Description(text: TextReference) {
@Composable @Composable
private fun Conditions(state: ResetCardScreenState) { private fun Conditions(state: ResetCardScreenState) {
state.warningsToShow.forEach { state.warningsToShow.forEach { warning ->
when (it) { when (warning) {
ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS -> { ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS -> {
ConditionCheckBox( ConditionCheckBox(
checkedState = state.acceptCondition1Checked, checkedState = state.isAcceptCondition1Checked,
onCheckedChange = state.onAcceptCondition1ToggleClick, onCheckedChange = state.onAcceptCondition1ToggleClick,
description = TextReference.Res(R.string.reset_card_to_factory_condition_1), description = TextReference.Res(R.string.reset_card_to_factory_condition_1),
) )
} }
ResetCardScreenState.WarningsToReset.LOST_PASSWORD_RESTORE -> { ResetCardScreenState.WarningsToReset.LOST_PASSWORD_RESTORE -> {
ConditionCheckBox( ConditionCheckBox(
checkedState = state.acceptCondition2Checked, checkedState = state.isAcceptCondition2Checked,
onCheckedChange = state.onAcceptCondition2ToggleClick, onCheckedChange = state.onAcceptCondition2ToggleClick,
description = TextReference.Res(R.string.reset_card_to_factory_condition_2), description = TextReference.Res(R.string.reset_card_to_factory_condition_2),
) )
@ -238,12 +238,12 @@ private fun ResetCardScreenSample(modifier: Modifier = Modifier) {
) { ) {
ResetCardScreen( ResetCardScreen(
state = ResetCardScreenState( state = ResetCardScreenState(
resetButtonEnabled = true, isResetButtonEnabled = true,
showResetPasswordButton = false, isResetPasswordButtonShown = false,
warningsToShow = listOf(ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS), warningsToShow = listOf(ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS),
descriptionText = TextReference.Res(R.string.reset_card_with_backup_to_factory_message), descriptionText = TextReference.Res(R.string.reset_card_with_backup_to_factory_message),
acceptCondition1Checked = false, isAcceptCondition1Checked = false,
acceptCondition2Checked = false, isAcceptCondition2Checked = false,
onAcceptCondition1ToggleClick = {}, onAcceptCondition1ToggleClick = {},
onAcceptCondition2ToggleClick = {}, onAcceptCondition2ToggleClick = {},
onResetButtonClick = {}, onResetButtonClick = {},

View file

@ -5,12 +5,12 @@ import com.tangem.tap.features.details.ui.cardsettings.TextReference
import com.tangem.wallet.R import com.tangem.wallet.R
internal data class ResetCardScreenState( internal data class ResetCardScreenState(
val resetButtonEnabled: Boolean, val isResetButtonEnabled: Boolean,
val descriptionText: TextReference, val descriptionText: TextReference,
val warningsToShow: List<WarningsToReset>, val warningsToShow: List<WarningsToReset>,
val showResetPasswordButton: Boolean, val isResetPasswordButtonShown: Boolean,
val acceptCondition1Checked: Boolean, val isAcceptCondition1Checked: Boolean,
val acceptCondition2Checked: Boolean, val isAcceptCondition2Checked: Boolean,
val onAcceptCondition1ToggleClick: (Boolean) -> Unit, val onAcceptCondition1ToggleClick: (Boolean) -> Unit,
val onAcceptCondition2ToggleClick: (Boolean) -> Unit, val onAcceptCondition2ToggleClick: (Boolean) -> Unit,
val onResetButtonClick: () -> Unit, val onResetButtonClick: () -> Unit,

View file

@ -97,15 +97,15 @@ internal class ResetCardModel @Inject constructor(
} }
return ResetCardScreenState( return ResetCardScreenState(
resetButtonEnabled = false, isResetButtonEnabled = false,
descriptionText = getResetToFactoryDescription( descriptionText = getResetToFactoryDescription(
isActiveBackupStatus = isActiveBackupPrimaryCard, isActiveBackupStatus = isActiveBackupPrimaryCard,
typesResolver = currentCardTypesResolver, typesResolver = currentCardTypesResolver,
), ),
warningsToShow = warningsToShow, warningsToShow = warningsToShow,
showResetPasswordButton = shouldShowResetPasswordButton, isResetPasswordButtonShown = shouldShowResetPasswordButton,
acceptCondition1Checked = false, isAcceptCondition1Checked = false,
acceptCondition2Checked = false, isAcceptCondition2Checked = false,
onAcceptCondition1ToggleClick = ::toggleFirstCondition, onAcceptCondition1ToggleClick = ::toggleFirstCondition,
onAcceptCondition2ToggleClick = ::toggleSecondCondition, onAcceptCondition2ToggleClick = ::toggleSecondCondition,
onResetButtonClick = { showDialog(ResetCardDialog.StartResetDialog) }, onResetButtonClick = { showDialog(ResetCardDialog.StartResetDialog) },
@ -121,26 +121,26 @@ internal class ResetCardModel @Inject constructor(
private fun toggleFirstCondition(isAccepted: Boolean) { private fun toggleFirstCondition(isAccepted: Boolean) {
screenState.update { prevState -> screenState.update { prevState ->
val resetButtonEnabled = if (prevState.showResetPasswordButton) { val isResetButtonEnabled = if (prevState.isResetPasswordButtonShown) {
isAccepted && prevState.acceptCondition2Checked isAccepted && prevState.isAcceptCondition2Checked
} else { } else {
isAccepted isAccepted
} }
prevState.copy( prevState.copy(
acceptCondition1Checked = isAccepted, isAcceptCondition1Checked = isAccepted,
resetButtonEnabled = resetButtonEnabled, isResetButtonEnabled = isResetButtonEnabled,
) )
} }
} }
private fun toggleSecondCondition(isAccepted: Boolean) { private fun toggleSecondCondition(isAccepted: Boolean) {
screenState.update { prevState -> screenState.update { prevState ->
val resetButtonEnabled = prevState.acceptCondition1Checked && isAccepted val isResetButtonEnabled = prevState.isAcceptCondition1Checked && isAccepted
prevState.copy( prevState.copy(
acceptCondition2Checked = isAccepted, isAcceptCondition2Checked = isAccepted,
resetButtonEnabled = resetButtonEnabled, isResetButtonEnabled = isResetButtonEnabled,
) )
} }
} }
@ -183,14 +183,14 @@ internal class ResetCardModel @Inject constructor(
modelScope.launch { modelScope.launch {
resetCardUseCase(cardId = primaryCardId, params = currentUserCodeParams).onRight { resetCardUseCase(cardId = primaryCardId, params = currentUserCodeParams).onRight {
deleteSavedAccessCodesUseCase(cardId = primaryCardId) deleteSavedAccessCodesUseCase(cardId = primaryCardId)
val hasUserWallets = deleteWalletUseCase(userWalletId = currentUserWalletId).getOrElse { val hasUserWallets = deleteWalletUseCase(userWalletId = currentUserWalletId).getOrElse { error ->
Timber.e("Unable to delete user wallet: $it") Timber.e("Unable to delete user wallet: $error")
return@launch return@launch
} }
if (hasUserWallets) { if (hasUserWallets) {
val newSelectedWallet = getSelectedWalletSyncUseCase().getOrElse { val newSelectedWallet = getSelectedWalletSyncUseCase().getOrElse { error ->
error("Failed to get selected wallet: $it") error("Failed to get selected wallet: $error")
} }
store.onUserWalletSelected(newSelectedWallet) store.onUserWalletSelected(newSelectedWallet)
@ -269,7 +269,7 @@ internal class ResetCardModel @Inject constructor(
if (hotWalletFeatureToggles.isHotWalletEnabled) { if (hotWalletFeatureToggles.isHotWalletEnabled) {
store.dispatchNavigationAction { replaceAll(AppRoute.Home()) } store.dispatchNavigationAction { replaceAll(AppRoute.Home()) }
} else { } else {
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }.isSuccess val isLocked = runCatching { userWalletsListManager.asLockable()?.isLocked }.isSuccess
if (isLocked && userWalletsListManager.hasUserWallets) { if (isLocked && userWalletsListManager.hasUserWallets) {
store.dispatchNavigationAction { popTo<AppRoute.Welcome>() } store.dispatchNavigationAction { popTo<AppRoute.Welcome>() }
} else { } else {

View file

@ -22,10 +22,10 @@ internal fun SecurityModeScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
SettingsScreensScaffold( SettingsScreensScaffold(
onBackClick = onBackClick,
modifier = modifier, modifier = modifier,
content = { SecurityModeOptions(state = state) }, content = { SecurityModeOptions(state = state) },
// titleRes = R.string.card_settings_security_mode, // titleRes = R.string.card_settings_security_mode,
onBackClick = onBackClick,
) )
} }
@ -57,7 +57,7 @@ private fun SecurityModeOptions(state: SecurityModeScreenState) {
@Composable @Composable
private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenState) { private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenState) {
val selected = option == state.selectedSecurityMode val isSelected = option == state.selectedSecurityMode
val title = option.toTitleRes() val title = option.toTitleRes()
@ -70,7 +70,7 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat
DetailsRadioButtonElement( DetailsRadioButtonElement(
title = stringResourceSafe(id = title), title = stringResourceSafe(id = title),
subtitle = stringResourceSafe(id = subtitle), subtitle = stringResourceSafe(id = subtitle),
selected = selected, isSelected = isSelected,
onClick = { state.onNewModeSelected(option) }, onClick = { state.onNewModeSelected(option) },
) )
} }

View file

@ -77,9 +77,9 @@ internal class SecurityModeModel @Inject constructor(
SecurityOption.AccessCode -> tangemSdkManager.setAccessCode(cardId) SecurityOption.AccessCode -> tangemSdkManager.setAccessCode(cardId)
} }
cardSettingsInteractor.update { cardSettingsInteractor.update { scanResponse ->
it.copy( scanResponse.copy(
card = it.card.copy( card = scanResponse.card.copy(
isAccessCodeSet = selectedOption == SecurityOption.AccessCode, isAccessCodeSet = selectedOption == SecurityOption.AccessCode,
isPasscodeSet = selectedOption == SecurityOption.PassCode, isPasscodeSet = selectedOption == SecurityOption.PassCode,
), ),

View file

@ -150,7 +150,7 @@ internal class MainViewModel @Inject constructor(
prepareSelectedWalletFeedback() prepareSelectedWalletFeedback()
// await while initial route stack is initialized // await while initial route stack is initialized
appRouterConfig.isInitialized.first { it } appRouterConfig.initializedState.first { it }
isSplashScreenShown = false isSplashScreenShown = false
} }
@ -179,11 +179,9 @@ internal class MainViewModel @Inject constructor(
private fun prepareSelectedWalletFeedback() { private fun prepareSelectedWalletFeedback() {
getSelectedWalletUseCase.invoke() getSelectedWalletUseCase.invoke()
.mapLeft { emptyFlow<UserWallet>() } .mapLeft { emptyFlow<UserWallet>() }
.onRight { .onRight { wallet ->
it.distinctUntilChanged() wallet.distinctUntilChanged()
.onEach { userWallet -> .onEach { Analytics.setContext(it) }
Analytics.setContext(userWallet)
}
.flowOn(dispatchers.io) .flowOn(dispatchers.io)
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
@ -208,7 +206,7 @@ internal class MainViewModel @Inject constructor(
return MoonPayService( return MoonPayService(
apiKey = environmentConfig.moonPayApiKey, apiKey = environmentConfig.moonPayApiKey,
secretKey = environmentConfig.moonPayApiSecretKey, secretKey = environmentConfig.moonPayApiSecretKey,
logEnabled = LogConfig.network.moonPayService, isLogEnabled = LogConfig.network.moonPayService,
userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() }, userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() },
) )
} }
@ -224,8 +222,8 @@ internal class MainViewModel @Inject constructor(
.filter { .filter {
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
} }
.onEach { .onEach { settings ->
if (!it.isUpdateFromToast) { if (!settings.isUpdateFromToast) {
listenToFlipsUseCase.changeUpdateEnabled(false) listenToFlipsUseCase.changeUpdateEnabled(false)
val message = BottomSheetMessage.invoke( val message = BottomSheetMessage.invoke(
@ -354,6 +352,7 @@ internal class MainViewModel @Inject constructor(
listenToFlipsUseCase.changeUpdateEnabled(isUpdateEnabled = true) listenToFlipsUseCase.changeUpdateEnabled(isUpdateEnabled = true)
} }
@Suppress("NullableToStringCall")
private fun sendKeyboardIdentifierEvent() { private fun sendKeyboardIdentifierEvent() {
viewModelScope.launch { viewModelScope.launch {
val keyboardId = keyboardValidator.getKeyboardId() val keyboardId = keyboardValidator.getKeyboardId()

View file

@ -34,11 +34,11 @@ object OnboardingHelper {
} }
response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> { response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> {
val emptyWallets = response.card.wallets.isEmpty() val areWalletsEmpty = response.card.wallets.isEmpty()
val activationInProgress = cardRepository.isActivationInProgress(cardId) val isActivationInProgress = cardRepository.isActivationInProgress(cardId)
val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup && val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup &&
!DemoHelper.isDemoCard(response) !DemoHelper.isDemoCard(response)
emptyWallets || activationInProgress || isNoBackup areWalletsEmpty || isActivationInProgress || isNoBackup
} }
response.card.wallets.isNotEmpty() -> cardRepository.isActivationInProgress(cardId) response.card.wallets.isNotEmpty() -> cardRepository.isActivationInProgress(cardId)

View file

@ -50,8 +50,8 @@ object TradeCryptoMiddleware {
fiatCurrencyName = action.appCurrencyCode, fiatCurrencyName = action.appCurrencyCode,
walletAddress = networkAddress, walletAddress = networkAddress,
isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive, isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive,
)?.let { )?.let { url ->
store.dispatchOpenUrl(it) store.dispatchOpenUrl(url)
Analytics.send(Token.Withdraw.ScreenOpened()) Analytics.send(Token.Withdraw.ScreenOpened())
} }
} }

View file

@ -72,8 +72,8 @@ internal class WelcomeModel @Inject constructor(
this.state.update { prevState -> this.state.update { prevState ->
prevState.copy( prevState.copy(
showUnlockWithBiometricsProgress = state.isUnlockWithBiometricsInProgress, isUnlockWithBiometricsProgressVisible = state.isUnlockWithBiometricsInProgress,
showUnlockWithCardProgress = state.isUnlockWithCardInProgress, isUnlockWithCardProgressVisible = state.isUnlockWithCardInProgress,
warning = warning, warning = warning,
error = state.error error = state.error
?.takeIf { !it.silent && warning == null } ?.takeIf { !it.silent && warning == null }

View file

@ -13,7 +13,7 @@ internal sealed interface WelcomeAction : Action {
object ProceedWithCard : WelcomeAction { object ProceedWithCard : WelcomeAction {
object Success : WelcomeAction object Success : WelcomeAction
data class Error(val error: TangemError) : WelcomeAction data class Error(val error: TangemError) : WelcomeAction
data class ChangeProgress(val showProgress: Boolean) : WelcomeAction data class ChangeProgress(val isProgress: Boolean) : WelcomeAction
} }
object CloseError : WelcomeAction object CloseError : WelcomeAction

View file

@ -141,13 +141,13 @@ internal class WelcomeMiddleware {
onSuccess = { scanResponse -> onSuccess = { scanResponse ->
scope.launch { onCardScanned(scanResponse) } scope.launch { onCardScanned(scanResponse) }
}, },
onFailure = { onFailure = { error ->
when (it) { when (error) {
is TangemSdkError.ExceptionError -> { is TangemSdkError.ExceptionError -> {
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success) store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
} }
else -> { else -> {
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(it)) store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(error))
} }
} }
}, },

View file

@ -25,7 +25,7 @@ internal object WelcomeReducer {
isUnlockWithCardInProgress = false, isUnlockWithCardInProgress = false,
) )
is WelcomeAction.ProceedWithCard.ChangeProgress -> state.copy( is WelcomeAction.ProceedWithCard.ChangeProgress -> state.copy(
isUnlockWithCardInProgress = action.showProgress, isUnlockWithCardInProgress = action.isProgress,
) )
is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false) is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false)
is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false) is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false)

View file

@ -5,8 +5,8 @@ import com.tangem.tap.features.welcome.ui.model.WarningModel
internal data class WelcomeScreenState( internal data class WelcomeScreenState(
val onPopBack: () -> Unit = {}, val onPopBack: () -> Unit = {},
val showUnlockWithBiometricsProgress: Boolean = false, val isUnlockWithBiometricsProgressVisible: Boolean = false,
val showUnlockWithCardProgress: Boolean = false, val isUnlockWithCardProgressVisible: Boolean = false,
val warning: WarningModel? = null, val warning: WarningModel? = null,
val error: TextReference? = null, val error: TextReference? = null,
val onUnlockClick: () -> Unit = {}, val onUnlockClick: () -> Unit = {},

View file

@ -39,8 +39,8 @@ internal fun WelcomeScreen(state: WelcomeScreenState, modifier: Modifier = Modif
.systemBarsPadding(), .systemBarsPadding(),
) { ) {
WelcomeScreenContent( WelcomeScreenContent(
showUnlockProgress = state.showUnlockWithBiometricsProgress, showUnlockProgress = state.isUnlockWithBiometricsProgressVisible,
showScanCardProgress = state.showUnlockWithCardProgress, showScanCardProgress = state.isUnlockWithCardProgressVisible,
onUnlockClick = state.onUnlockClick, onUnlockClick = state.onUnlockClick,
onScanCardClick = state.onScanCardClick, onScanCardClick = state.onScanCardClick,
) )
@ -57,8 +57,8 @@ internal fun WelcomeScreen(state: WelcomeScreenState, modifier: Modifier = Modif
WarningDialog(warning) WarningDialog(warning)
LaunchedEffect(errorMessage, state.onCloseError) { LaunchedEffect(errorMessage, state.onCloseError) {
errorMessage?.let { errorMessage?.let { message ->
snackbarHostState.showSnackbar(it) snackbarHostState.showSnackbar(message)
state.onCloseError() state.onCloseError()
} }
} }
@ -82,8 +82,8 @@ private class WelcomeComponentPreviewProvider : PreviewParameterProvider<Welcome
PreviewWelcomeComponent(), PreviewWelcomeComponent(),
PreviewWelcomeComponent( PreviewWelcomeComponent(
initialState = WelcomeScreenState( initialState = WelcomeScreenState(
showUnlockWithBiometricsProgress = true, isUnlockWithBiometricsProgressVisible = true,
showUnlockWithCardProgress = true, isUnlockWithCardProgressVisible = true,
), ),
), ),
PreviewWelcomeComponent( PreviewWelcomeComponent(

View file

@ -9,7 +9,7 @@ import com.tangem.domain.core.wallets.UserWalletsListRepository
internal class DefaultAuthProvider( internal class DefaultAuthProvider(
private val userWalletsListManager: UserWalletsListManager, private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository, private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean = false, private val shouldUseNewListRepository: Boolean = false,
) : AuthProvider { ) : AuthProvider {
override suspend fun getCardPublicKey(): String { override suspend fun getCardPublicKey(): String {
@ -39,7 +39,7 @@ internal class DefaultAuthProvider(
} }
private suspend fun getWallets(): List<UserWallet> { private suspend fun getWallets(): List<UserWallet> {
return if (useNewListRepository) { return if (shouldUseNewListRepository) {
userWalletsListRepository.userWalletsSync() userWalletsListRepository.userWalletsSync()
} else { } else {
userWalletsListManager.userWalletsSync userWalletsListManager.userWalletsSync
@ -47,7 +47,7 @@ internal class DefaultAuthProvider(
} }
private suspend fun getSelectedWallet(): UserWallet? { private suspend fun getSelectedWallet(): UserWallet? {
return if (useNewListRepository) { return if (shouldUseNewListRepository) {
userWalletsListRepository.selectedUserWalletSync() userWalletsListRepository.selectedUserWalletSync()
} else { } else {
userWalletsListManager.selectedUserWalletSync userWalletsListManager.selectedUserWalletSync

View file

@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicReference
internal class DefaultExpressAuthProvider : ExpressAuthProvider { internal class DefaultExpressAuthProvider : ExpressAuthProvider {
private var uuid = AtomicReference(UUID.randomUUID()) private val uuid = AtomicReference(UUID.randomUUID())
override fun getSessionId(): String { override fun getSessionId(): String {
return uuid.get().toString() return uuid.get().toString()

View file

@ -32,7 +32,7 @@ internal class AuthModule {
return DefaultAuthProvider( return DefaultAuthProvider(
userWalletsListManager = userWalletsListManager, userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository, userWalletsListRepository = userWalletsListRepository,
useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled, shouldUseNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled,
) )
} }

View file

@ -54,7 +54,7 @@ internal class DefaultRampManager(
sendUnavailabilityReason: ScenarioUnavailabilityReason?, sendUnavailabilityReason: ScenarioUnavailabilityReason?,
): Either<ScenarioUnavailabilityReason, Unit> { ): Either<ScenarioUnavailabilityReason, Unit> {
return either { return either {
val sellSupportedByService = catch( val isSellSupportedByService = catch(
block = { block = {
val serviceCurrency = cryptoCurrencyConverter.convertBack(status.currency) val serviceCurrency = cryptoCurrencyConverter.convertBack(status.currency)
@ -78,7 +78,7 @@ internal class DefaultRampManager(
} }
} }
ensure(condition = sellSupportedByService) { ensure(condition = isSellSupportedByService) {
ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name) ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)
} }

View file

@ -34,6 +34,7 @@ data class MoonPayUserStatus(
val stateCode: String, val stateCode: String,
) )
@Suppress("BooleanPropertyNaming")
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class MoonPayCurrencies( data class MoonPayCurrencies(
@Json(name = "type") val type: String, @Json(name = "type") val type: String,

View file

@ -26,7 +26,7 @@ import javax.crypto.spec.SecretKeySpec
class MoonPayService( class MoonPayService(
private val apiKey: String, private val apiKey: String,
private val secretKey: String, private val secretKey: String,
private val logEnabled: Boolean, private val isLogEnabled: Boolean,
private val userWalletProvider: () -> UserWallet?, private val userWalletProvider: () -> UserWallet?,
) : ExchangeService { ) : ExchangeService {
@ -39,7 +39,7 @@ class MoonPayService(
private val api: MoonPayApi by lazy { private val api: MoonPayApi by lazy {
createRetrofitInstance( createRetrofitInstance(
baseUrl = MoonPayApi.MOOONPAY_BASE_URL, baseUrl = MoonPayApi.MOOONPAY_BASE_URL,
logEnabled = logEnabled, logEnabled = isLogEnabled,
).create(MoonPayApi::class.java) ).create(MoonPayApi::class.java)
} }
@ -104,24 +104,35 @@ class MoonPayService(
override fun availableForSell(currency: Currency): Boolean { override fun availableForSell(currency: Currency): Boolean {
val userWallet = userWalletProvider() ?: return false val userWallet = userWalletProvider() ?: return false
val checkCardExchange = userWallet !is UserWallet.Cold || !userWallet.scanResponse.card.isStart2Coin val isExchangeSupported = userWallet !is UserWallet.Cold || !userWallet.scanResponse.card.isStart2Coin
if (!checkCardExchange) return false if (!isExchangeSupported) return false
if (!isSellAllowed()) return false if (!isSellAllowed()) return false
val availableForSell = status?.availableForSell ?: return false val availableForSell = status?.availableForSell ?: return false
val supportedCurrency = currency.blockchain.moonPaySupportedCurrency ?: return false val supportedCurrency = currency.blockchain.moonPaySupportedCurrency ?: return false
return availableForSell.any { return availableForSell.any { availableCurrency ->
when (currency) { when (currency) {
is Currency.Blockchain -> { is Currency.Blockchain -> {
it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && availableCurrency.networkCode.equals(
it.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true) other = supportedCurrency.networkCode,
ignoreCase = true,
) && availableCurrency.currencyCode.equals(
other = supportedCurrency.currencyCode,
ignoreCase = true,
)
} }
is Currency.Token -> { is Currency.Token -> {
it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && availableCurrency.networkCode.equals(
it.contractAddress.equals(other = currency.token.contractAddress, ignoreCase = true) other = supportedCurrency.networkCode,
ignoreCase = true,
) &&
availableCurrency.contractAddress.equals(
other = currency.token.contractAddress,
ignoreCase = true,
)
} }
} }
} }
@ -137,15 +148,18 @@ class MoonPayService(
if (blockchain.isTestnet()) return blockchain.getTestnetTopUpUrl() if (blockchain.isTestnet()) return blockchain.getTestnetTopUpUrl()
val supportedCurrency = blockchain.moonPaySupportedCurrency ?: return null val supportedCurrency = blockchain.moonPaySupportedCurrency ?: return null
val moonpayCurrency = status?.availableForSell?.firstOrNull { val moonpayCurrency = status?.availableForSell?.firstOrNull { availableCurrency ->
when (cryptoCurrency) { when (cryptoCurrency) {
is CryptoCurrency.Coin -> { is CryptoCurrency.Coin -> {
it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && availableCurrency.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) &&
it.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true) availableCurrency.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true)
} }
is CryptoCurrency.Token -> { is CryptoCurrency.Token -> {
it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && availableCurrency.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) &&
it.contractAddress.equals(other = cryptoCurrency.contractAddress, ignoreCase = true) availableCurrency.contractAddress.equals(
other = cryptoCurrency.contractAddress,
ignoreCase = true,
)
} }
} }
} ?: return null } ?: return null
@ -184,7 +198,7 @@ class MoonPayService(
} }
private fun isSellAllowed(): Boolean { private fun isSellAllowed(): Boolean {
return status?.responseUserStatus?.isSellAllowed ?: false return status?.responseUserStatus?.isSellAllowed == true
} }
private companion object { private companion object {

View file

@ -48,13 +48,13 @@ class UserWalletManagerImpl(
override suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? { override suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain, derivationPath) val walletManager = getActualWalletManager(blockchain, derivationPath)
return walletManager.wallet.amounts.firstNotNullOfOrNull { return walletManager.wallet.amounts.firstNotNullOfOrNull { amountEntry ->
it.takeIf { it.key is AmountType.Coin } amountEntry.takeIf { amountEntry.key is AmountType.Coin }
}?.value?.let { }?.value?.let { amount ->
ProxyAmount( ProxyAmount(
it.currencySymbol, amount.currencySymbol,
it.value ?: BigDecimal.ZERO, amount.value ?: BigDecimal.ZERO,
it.decimals, amount.decimals,
) )
} }
} }

View file

@ -34,17 +34,17 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.routing.component.RoutingComponent import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory
@Suppress("LongParameterList") @Suppress("LongParameterList", "ReusedModifierInstance")
@OptIn(ExperimentalDecomposeApi::class) @OptIn(ExperimentalDecomposeApi::class)
@Composable @Composable
internal fun RootContent( internal fun RootContent(
stack: Value<ChildStack<AppRoute, RoutingComponent.Child>>, stack: Value<ChildStack<AppRoute, RoutingComponent.Child>>,
backHandler: BackHandler, backHandler: BackHandler,
uiDependencies: UiDependencies, uiDependencies: UiDependencies,
wcContent: @Composable (modifier: Modifier) -> Unit,
hotAccessCodeContent: @Composable (modifier: Modifier) -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
wcContent: @Composable (modifier: Modifier) -> Unit,
hotAccessCodeContent: @Composable (modifier: Modifier) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current

View file

@ -133,7 +133,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
AppRoute.Wallet AppRoute.Wallet
} }
}.also { }.also {
appRouterConfig.isInitialized.value = true appRouterConfig.initializedState.value = true
checkForUnfinishedBackup() checkForUnfinishedBackup()
} }
} }
@ -141,13 +141,13 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
RootContent( RootContent(
modifier = modifier,
stack = stack, stack = stack,
backHandler = backHandler,
uiDependencies = uiDependencies, uiDependencies = uiDependencies,
onBack = router::pop,
modifier = modifier,
wcContent = { wcRoutingComponent.Content(it) }, wcContent = { wcRoutingComponent.Content(it) },
hotAccessCodeContent = { hotAccessCodeRequestComponent.Content(it) }, hotAccessCodeContent = { hotAccessCodeRequestComponent.Content(it) },
backHandler = backHandler,
onBack = router::pop,
) )
} }

View file

@ -11,7 +11,7 @@ internal interface AppRouterConfig {
var routerScope: CoroutineScope? var routerScope: CoroutineScope?
var componentRouter: Router? var componentRouter: Router?
var stack: List<AppRoute>? var stack: List<AppRoute>?
val isInitialized: MutableStateFlow<Boolean> val initializedState: MutableStateFlow<Boolean>
// TODO: Replace with UI message handler: [REDACTED_JIRA] // TODO: Replace with UI message handler: [REDACTED_JIRA]
var snackbarHandler: SnackbarHandler? var snackbarHandler: SnackbarHandler?

View file

@ -11,5 +11,5 @@ internal class MutableAppRouterConfig : AppRouterConfig {
override var componentRouter: Router? = null override var componentRouter: Router? = null
override var stack: List<AppRoute>? = null override var stack: List<AppRoute>? = null
override var snackbarHandler: SnackbarHandler? = null override var snackbarHandler: SnackbarHandler? = null
override val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false) override val initializedState: MutableStateFlow<Boolean> = MutableStateFlow(false)
} }

View file

@ -4,13 +4,10 @@ import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.layout
import com.arkivanov.decompose.extensions.compose.stack.animation.* import com.arkivanov.decompose.extensions.compose.stack.animation.*
import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRoute
import kotlin.compareTo
import kotlin.times
object RoutingTransitionAnimationFactory { object RoutingTransitionAnimationFactory {
@ -58,7 +55,7 @@ object RoutingTransitionAnimationFactory {
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator { private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator {
val easing = CubicBezierEasing(0.55f, 0.0f, 0.0f, 1f) val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f)
return stackAnimator( return stackAnimator(
animationSpec = tween(durationMillis = 400, easing = easing), animationSpec = tween(durationMillis = 400, easing = easing),

View file

@ -183,10 +183,10 @@ internal class ChildFactory @Inject constructor(
token = route.token, token = route.token,
appCurrency = route.appCurrency, appCurrency = route.appCurrency,
showPortfolio = route.showPortfolio, showPortfolio = route.showPortfolio,
analyticsParams = route.analyticsParams?.let { analyticsParams = route.analyticsParams?.let { params ->
MarketsTokenDetailsComponent.AnalyticsParams( MarketsTokenDetailsComponent.AnalyticsParams(
blockchain = it.blockchain, blockchain = params.blockchain,
source = it.source, source = params.source,
) )
}, },
), ),

View file

@ -311,7 +311,6 @@ private fun MarketChartPreview(
} }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val look by dataProducer.lookState.collectAsState()
TangemThemePreview { TangemThemePreview {
val growingColor = TangemTheme.colors.icon.accent val growingColor = TangemTheme.colors.icon.accent

Some files were not shown because too many files have changed in this diff Show more