Updated on 2026-08-14
This commit is contained in:
parent
5ef3837bd5
commit
8d3c2b1cd8
23 changed files with 413 additions and 46 deletions
|
|
@ -10,6 +10,7 @@ import com.tangem.sdk.api.TangemSdkManager
|
||||||
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
|
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
|
||||||
import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
|
import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
|
||||||
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
||||||
|
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateVirtualAccountAddressTask
|
||||||
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
||||||
import com.tangem.tap.domain.visa.VisaCardScanHandler
|
import com.tangem.tap.domain.visa.VisaCardScanHandler
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -31,6 +32,7 @@ internal class TangemSdkManagerModule {
|
||||||
visaCardScanHandler: VisaCardScanHandler,
|
visaCardScanHandler: VisaCardScanHandler,
|
||||||
visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
||||||
tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
||||||
|
tangemPayVirtualAccountTaskFactory: TangemPayGenerateVirtualAccountAddressTask.Factory,
|
||||||
onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||||
analyticsErrorHandler: AnalyticsErrorHandler,
|
analyticsErrorHandler: AnalyticsErrorHandler,
|
||||||
cardRepository: CardRepository,
|
cardRepository: CardRepository,
|
||||||
|
|
@ -44,6 +46,7 @@ internal class TangemSdkManagerModule {
|
||||||
visaCardScanHandler = visaCardScanHandler,
|
visaCardScanHandler = visaCardScanHandler,
|
||||||
visaCardActivationTaskFactory = visaCardActivationTaskFactory,
|
visaCardActivationTaskFactory = visaCardActivationTaskFactory,
|
||||||
tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory,
|
tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory,
|
||||||
|
tangemPayVirtualAccountTaskFactory = tangemPayVirtualAccountTaskFactory,
|
||||||
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
||||||
analyticsErrorHandler = analyticsErrorHandler,
|
analyticsErrorHandler = analyticsErrorHandler,
|
||||||
cardRepository = cardRepository,
|
cardRepository = cardRepository,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
|
||||||
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
|
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
|
||||||
import com.tangem.tap.domain.tasks.product.*
|
import com.tangem.tap.domain.tasks.product.*
|
||||||
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
||||||
|
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateVirtualAccountAddressTask
|
||||||
import com.tangem.tap.domain.tasks.visa.TangemPaySignWithdrawalHashTask
|
import com.tangem.tap.domain.tasks.visa.TangemPaySignWithdrawalHashTask
|
||||||
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
||||||
import com.tangem.tap.domain.tasks.visa.VisaCustomerWalletApproveTask
|
import com.tangem.tap.domain.tasks.visa.VisaCustomerWalletApproveTask
|
||||||
|
|
@ -72,6 +73,7 @@ internal class DefaultTangemSdkManager(
|
||||||
private val visaCardScanHandler: VisaCardScanHandler,
|
private val visaCardScanHandler: VisaCardScanHandler,
|
||||||
private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
||||||
private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
||||||
|
private val tangemPayVirtualAccountTaskFactory: TangemPayGenerateVirtualAccountAddressTask.Factory,
|
||||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||||
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
||||||
private val cardRepository: CardRepository,
|
private val cardRepository: CardRepository,
|
||||||
|
|
@ -531,6 +533,24 @@ internal class DefaultTangemSdkManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun tangemPayProduceVirtualAccountData(
|
||||||
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
): Either<Throwable, VirtualAccountActivationData> {
|
||||||
|
return coroutineScope {
|
||||||
|
val result = runTaskAsyncReturnOnMain(
|
||||||
|
runnable = tangemPayVirtualAccountTaskFactory.create(coroutineScope = this),
|
||||||
|
cardId = null,
|
||||||
|
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
|
||||||
|
preflightReadFilter = preflightReadFilter,
|
||||||
|
)
|
||||||
|
|
||||||
|
return@coroutineScope when (result) {
|
||||||
|
is CompletionResult.Failure<*> -> result.error.left()
|
||||||
|
is CompletionResult.Success<VirtualAccountActivationData> -> result.data.right()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getWithdrawalSignature(
|
override suspend fun getWithdrawalSignature(
|
||||||
hash: String,
|
hash: String,
|
||||||
preflightReadFilter: PreflightReadFilter,
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.WithdrawalSignatureResult
|
import com.tangem.domain.pay.WithdrawalSignatureResult
|
||||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
import com.tangem.domain.visa.model.VisaActivationInput
|
import com.tangem.domain.visa.model.VisaActivationInput
|
||||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||||
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
||||||
|
|
@ -241,6 +242,12 @@ class MockTangemSdkManager(
|
||||||
error("Not implemented")
|
error("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun tangemPayProduceVirtualAccountData(
|
||||||
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
): Either<Throwable, VirtualAccountActivationData> {
|
||||||
|
error("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getWithdrawalSignature(
|
override suspend fun getWithdrawalSignature(
|
||||||
hash: String,
|
hash: String,
|
||||||
preflightReadFilter: PreflightReadFilter,
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.tangem.tap.domain.tasks.visa
|
||||||
|
|
||||||
|
import com.tangem.common.CompletionResult
|
||||||
|
import com.tangem.common.card.CardWallet
|
||||||
|
import com.tangem.common.core.CardSession
|
||||||
|
import com.tangem.common.core.CardSessionRunnable
|
||||||
|
import com.tangem.common.core.CompletionCallback
|
||||||
|
import com.tangem.common.core.TangemSdkError
|
||||||
|
import com.tangem.common.extensions.toMapKey
|
||||||
|
import com.tangem.core.error.ext.tangemError
|
||||||
|
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||||
|
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||||
|
import com.tangem.domain.visa.error.VisaActivationError
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
|
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||||
|
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the Virtual Account key ([VisaUtilities.virtualAccountDerivationPath]) on the card and
|
||||||
|
* generates its deposit address. The derived key is returned (keyed by the seed wallet public key)
|
||||||
|
* so the caller can persist it via `DerivationsRepository.storeDerivedKeys` — no second tap needed.
|
||||||
|
*/
|
||||||
|
class TangemPayGenerateVirtualAccountAddressTask @AssistedInject constructor(
|
||||||
|
@Assisted private val coroutineScope: CoroutineScope,
|
||||||
|
) : CardSessionRunnable<VirtualAccountActivationData> {
|
||||||
|
|
||||||
|
override fun run(session: CardSession, callback: CompletionCallback<VirtualAccountActivationData>) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
callback(runSuspend(session = session))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun runSuspend(session: CardSession): CompletionResult<VirtualAccountActivationData> {
|
||||||
|
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||||
|
val wallet = card.wallets.firstOrNull { it.curve == VisaUtilities.curve }
|
||||||
|
?: return CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError)
|
||||||
|
|
||||||
|
val extendedPublicKey = when (val derivationResult = runDerivationTask(session, wallet)) {
|
||||||
|
is CompletionResult.Failure<*> -> return CompletionResult.Failure(derivationResult.error)
|
||||||
|
is CompletionResult.Success<ExtendedPublicKey> -> derivationResult.data
|
||||||
|
}
|
||||||
|
|
||||||
|
val address = VisaUtilities.generateAddressFromExtendedKey(extendedPublicKey = extendedPublicKey)
|
||||||
|
|
||||||
|
val derivedKeys = mapOf(
|
||||||
|
wallet.publicKey.toMapKey() to ExtendedPublicKeysMap(
|
||||||
|
mapOf(VisaUtilities.virtualAccountDerivationPath to extendedPublicKey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return CompletionResult.Success(
|
||||||
|
data = VirtualAccountActivationData(address = address, derivedKeys = derivedKeys),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun runDerivationTask(
|
||||||
|
session: CardSession,
|
||||||
|
wallet: CardWallet,
|
||||||
|
): CompletionResult<ExtendedPublicKey> {
|
||||||
|
val deferred = CompletableDeferred<CompletionResult<ExtendedPublicKey>>()
|
||||||
|
val derivationTask = DeriveWalletPublicKeyTask(
|
||||||
|
walletPublicKey = wallet.publicKey,
|
||||||
|
derivationPath = VisaUtilities.virtualAccountDerivationPath,
|
||||||
|
)
|
||||||
|
|
||||||
|
derivationTask.run(session = session, callback = deferred::complete)
|
||||||
|
return deferred.await()
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(coroutineScope: CoroutineScope): TangemPayGenerateVirtualAccountAddressTask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.pay.WithdrawalSignatureResult
|
import com.tangem.domain.pay.WithdrawalSignatureResult
|
||||||
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -26,6 +27,18 @@ internal class DefaultTangemPayAuthDataSource @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun produceVirtualAccountData(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
): Either<Throwable, VirtualAccountActivationData> {
|
||||||
|
return when (userWallet) {
|
||||||
|
is UserWallet.Cold -> {
|
||||||
|
val preflightReadFilter = UserWalletIdPreflightReadFilter(userWallet.walletId)
|
||||||
|
tangemSdkManager.tangemPayProduceVirtualAccountData(preflightReadFilter = preflightReadFilter)
|
||||||
|
}
|
||||||
|
is UserWallet.Hot -> tangemPayHotSdkManager.produceVirtualAccountData(userWallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getWithdrawalSignature(
|
override suspend fun getWithdrawalSignature(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
hash: String,
|
hash: String,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import arrow.core.getOrElse
|
||||||
import arrow.core.raise.Raise
|
import arrow.core.raise.Raise
|
||||||
import arrow.core.raise.either
|
import arrow.core.raise.either
|
||||||
import com.tangem.common.extensions.hexToBytes
|
import com.tangem.common.extensions.hexToBytes
|
||||||
|
import com.tangem.common.extensions.toMapKey
|
||||||
import com.tangem.core.error.ext.tangemError
|
import com.tangem.core.error.ext.tangemError
|
||||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||||
|
|
@ -14,11 +15,13 @@ import com.tangem.domain.visa.datasource.TangemPayRemoteDataSource
|
||||||
import com.tangem.domain.visa.error.VisaActivationError
|
import com.tangem.domain.visa.error.VisaActivationError
|
||||||
import com.tangem.domain.visa.error.VisaCardScanError
|
import com.tangem.domain.visa.error.VisaCardScanError
|
||||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||||
import com.tangem.hot.sdk.TangemHotSdk
|
import com.tangem.hot.sdk.TangemHotSdk
|
||||||
import com.tangem.hot.sdk.model.DataToSign
|
import com.tangem.hot.sdk.model.DataToSign
|
||||||
import com.tangem.hot.sdk.model.DeriveWalletRequest
|
import com.tangem.hot.sdk.model.DeriveWalletRequest
|
||||||
import com.tangem.hot.sdk.model.UnlockHotWallet
|
import com.tangem.hot.sdk.model.UnlockHotWallet
|
||||||
|
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class TangemPayHotSdkManager @Inject constructor(
|
internal class TangemPayHotSdkManager @Inject constructor(
|
||||||
|
|
@ -56,6 +59,34 @@ internal class TangemPayHotSdkManager @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun produceVirtualAccountData(hotWallet: UserWallet.Hot): Either<Throwable, VirtualAccountActivationData> =
|
||||||
|
withUnlockedHotWallet(hotWallet) { unlockHotWallet ->
|
||||||
|
val response = tangemHotSdk.derivePublicKey(
|
||||||
|
unlockHotWallet = unlockHotWallet,
|
||||||
|
request = DeriveWalletRequest(
|
||||||
|
requests = listOf(
|
||||||
|
DeriveWalletRequest.Request(
|
||||||
|
curve = VisaUtilities.curve,
|
||||||
|
paths = listOf(VisaUtilities.virtualAccountDerivationPath),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val curveResponse = response.responses.firstOrNull { it.curve == VisaUtilities.curve }
|
||||||
|
?: raise(VisaActivationError.MissingWallet.tangemError)
|
||||||
|
val extendedPublicKey = curveResponse.publicKeys[VisaUtilities.virtualAccountDerivationPath]
|
||||||
|
?: raise(VisaActivationError.MissingWallet.tangemError)
|
||||||
|
|
||||||
|
VirtualAccountActivationData(
|
||||||
|
address = VisaUtilities.generateAddressFromExtendedKey(extendedPublicKey),
|
||||||
|
derivedKeys = mapOf(
|
||||||
|
curveResponse.seedKey.publicKey.toMapKey() to ExtendedPublicKeysMap(
|
||||||
|
mapOf(VisaUtilities.virtualAccountDerivationPath to extendedPublicKey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getWithdrawalSignature(
|
suspend fun getWithdrawalSignature(
|
||||||
hotWallet: UserWallet.Hot,
|
hotWallet: UserWallet.Hot,
|
||||||
hash: String,
|
hash: String,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.squareup.moshi.Moshi
|
||||||
import com.tangem.data.virtualaccount.converter.VirtualAccountStatusValueDMConverter
|
import com.tangem.data.virtualaccount.converter.VirtualAccountStatusValueDMConverter
|
||||||
import com.tangem.data.virtualaccount.flow.DefaultVirtualAccountStatusFetcher
|
import com.tangem.data.virtualaccount.flow.DefaultVirtualAccountStatusFetcher
|
||||||
import com.tangem.data.virtualaccount.flow.DefaultVirtualAccountStatusProducer
|
import com.tangem.data.virtualaccount.flow.DefaultVirtualAccountStatusProducer
|
||||||
|
import com.tangem.data.virtualaccount.repository.DefaultVirtualAccountActivationRepository
|
||||||
import com.tangem.data.virtualaccount.store.VirtualAccountStatusesStore
|
import com.tangem.data.virtualaccount.store.VirtualAccountStatusesStore
|
||||||
import com.tangem.datasource.di.NetworkMoshi
|
import com.tangem.datasource.di.NetworkMoshi
|
||||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||||
|
|
@ -17,6 +18,8 @@ import com.tangem.datasource.utils.mapWithStringKeyTypes
|
||||||
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
||||||
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusProducer
|
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusProducer
|
||||||
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusSupplier
|
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusSupplier
|
||||||
|
import com.tangem.domain.virtualaccount.repository.VirtualAccountActivationRepository
|
||||||
|
import com.tangem.domain.virtualaccount.usecase.ActivateVirtualAccountUseCase
|
||||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -40,6 +43,12 @@ internal interface VirtualAccountDataModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun bindVirtualAccountStatusFetcher(impl: DefaultVirtualAccountStatusFetcher): VirtualAccountStatusFetcher
|
fun bindVirtualAccountStatusFetcher(impl: DefaultVirtualAccountStatusFetcher): VirtualAccountStatusFetcher
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindVirtualAccountActivationRepository(
|
||||||
|
impl: DefaultVirtualAccountActivationRepository,
|
||||||
|
): VirtualAccountActivationRepository
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
@ -77,5 +86,13 @@ internal interface VirtualAccountDataModule {
|
||||||
keyCreator = { "virtual_account_status_${it.userWalletId.stringValue}" },
|
keyCreator = { "virtual_account_status_${it.userWalletId.stringValue}" },
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideActivateVirtualAccountUseCase(
|
||||||
|
repository: VirtualAccountActivationRepository,
|
||||||
|
): ActivateVirtualAccountUseCase {
|
||||||
|
return ActivateVirtualAccountUseCase(repository = repository)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,6 @@ import com.tangem.domain.networks.single.SingleNetworkStatusProducer
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||||
import com.tangem.domain.pay.TangemPayCurrencyFactory
|
import com.tangem.domain.pay.TangemPayCurrencyFactory
|
||||||
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
||||||
import com.tangem.domain.wallets.extension.hasDerivation
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
@ -57,21 +56,9 @@ internal class DefaultVirtualAccountStatusFetcher @Inject constructor(
|
||||||
|
|
||||||
private suspend fun getBalance(userWalletId: UserWalletId): Either<VirtualAccountStatusValue, BigDecimal> {
|
private suspend fun getBalance(userWalletId: UserWalletId): Either<VirtualAccountStatusValue, BigDecimal> {
|
||||||
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
||||||
|
|
||||||
val hasVirtualAccountDerivation = userWallet.hasDerivation(
|
|
||||||
blockchain = VisaUtilities.visaBlockchain,
|
|
||||||
derivationPath = VisaUtilities.virtualAccountDerivationPath.rawPath,
|
|
||||||
)
|
|
||||||
if (!hasVirtualAccountDerivation) {
|
|
||||||
// TODO: Doston(VA) Derive will be implemented in [REDACTED_TASK_KEY]
|
|
||||||
TangemLogger.withTag(TAG).d("Virtual account is not derived")
|
|
||||||
return VirtualAccountStatusValue.Error.NotSynced.left()
|
|
||||||
}
|
|
||||||
|
|
||||||
val network = networkFactory.create(
|
val network = networkFactory.create(
|
||||||
blockchain = VisaUtilities.visaBlockchain,
|
blockchain = VisaUtilities.visaBlockchain,
|
||||||
derivationPath =
|
derivationPath = Network.DerivationPath.Custom(VisaUtilities.virtualAccountDerivationPath.rawPath),
|
||||||
Network.DerivationPath.Custom(VisaUtilities.virtualAccountDerivationPath.rawPath),
|
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
)
|
)
|
||||||
if (network == null) {
|
if (network == null) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.tangem.data.virtualaccount.repository
|
||||||
|
|
||||||
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
|
import com.tangem.domain.common.wallets.getSyncStrict
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
|
import com.tangem.domain.virtualaccount.repository.VirtualAccountActivationRepository
|
||||||
|
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class DefaultVirtualAccountActivationRepository @Inject constructor(
|
||||||
|
private val authDataSource: TangemPayAuthDataSource,
|
||||||
|
private val derivationsRepository: DerivationsRepository,
|
||||||
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) : VirtualAccountActivationRepository {
|
||||||
|
|
||||||
|
override suspend fun activateVirtualAccount(userWalletId: UserWalletId) {
|
||||||
|
withContext(dispatchers.io) {
|
||||||
|
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
||||||
|
val activationData = authDataSource.produceVirtualAccountData(userWallet)
|
||||||
|
.fold(
|
||||||
|
ifLeft = { error("Can not activate virtual account: ${it.message}") },
|
||||||
|
ifRight = { it },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Persist the derived VA key so the on-chain balance can be read without re-deriving (no extra tap).
|
||||||
|
derivationsRepository.storeDerivedKeys(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
derivedKeys = activationData.derivedKeys,
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO([REDACTED_TASK_KEY]): register activationData.address with the VA backend once the endpoint is available.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,24 +13,14 @@ import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||||
import com.tangem.domain.pay.TangemPayCurrencyFactory
|
import com.tangem.domain.pay.TangemPayCurrencyFactory
|
||||||
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
|
||||||
import com.tangem.domain.wallets.extension.hasDerivation
|
|
||||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
import io.mockk.clearMocks
|
import io.mockk.*
|
||||||
import io.mockk.coEvery
|
|
||||||
import io.mockk.coVerify
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.mockkStatic
|
|
||||||
import io.mockk.unmockkStatic
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.AfterEach
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
private const val USER_WALLET_EXTENSIONS = "com.tangem.domain.wallets.extension.UserWalletExtensionsKt"
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
internal class DefaultVirtualAccountStatusFetcherTest {
|
internal class DefaultVirtualAccountStatusFetcherTest {
|
||||||
|
|
||||||
|
|
@ -59,25 +49,40 @@ internal class DefaultVirtualAccountStatusFetcherTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
mockkStatic(USER_WALLET_EXTENSIONS)
|
|
||||||
clearMocks(networkFactory, tangemPayCurrencyFactory, singleNetworkStatusFetcher, userWalletsListRepository)
|
clearMocks(networkFactory, tangemPayCurrencyFactory, singleNetworkStatusFetcher, userWalletsListRepository)
|
||||||
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
||||||
every {
|
|
||||||
networkFactory.create(any<Blockchain>(), any<Network.DerivationPath>(), any<UserWallet>())
|
|
||||||
} returns network
|
|
||||||
every { tangemPayCurrencyFactory.createVirtualAccountToken(userWalletId) } returns token
|
every { tangemPayCurrencyFactory.createVirtualAccountToken(userWalletId) } returns token
|
||||||
coEvery { singleNetworkStatusFetcher(any()) } returns Unit.right()
|
coEvery { singleNetworkStatusFetcher(any()) } returns Unit.right()
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@Test
|
||||||
fun tearDown() {
|
fun `GIVEN network created WHEN invoke THEN on-chain status fetched with VA token`() = runTest {
|
||||||
unmockkStatic(USER_WALLET_EXTENSIONS)
|
// Arrange
|
||||||
|
every {
|
||||||
|
networkFactory.create(any<Blockchain>(), any<Network.DerivationPath>(), any<UserWallet>())
|
||||||
|
} returns network
|
||||||
|
|
||||||
|
// Act
|
||||||
|
fetcher.invoke(VirtualAccountStatusFetcher.Params(userWalletId))
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerify(exactly = 1) {
|
||||||
|
singleNetworkStatusFetcher(
|
||||||
|
SingleNetworkStatusFetcher.Params(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
network = network,
|
||||||
|
extraTokens = setOf(token),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN VA derivation missing WHEN invoke THEN on-chain fetch skipped`() = runTest {
|
fun `GIVEN network cannot be created WHEN invoke THEN on-chain fetch skipped`() = runTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
every { userWallet.hasDerivation(any(), any()) } returns false
|
every {
|
||||||
|
networkFactory.create(any<Blockchain>(), any<Network.DerivationPath>(), any<UserWallet>())
|
||||||
|
} returns null
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
fetcher.invoke(VirtualAccountStatusFetcher.Params(userWalletId))
|
fetcher.invoke(VirtualAccountStatusFetcher.Params(userWalletId))
|
||||||
|
|
@ -85,16 +90,4 @@ internal class DefaultVirtualAccountStatusFetcherTest {
|
||||||
// Assert
|
// Assert
|
||||||
coVerify(exactly = 0) { singleNetworkStatusFetcher(any()) }
|
coVerify(exactly = 0) { singleNetworkStatusFetcher(any()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN VA derivation present WHEN invoke THEN on-chain fetch performed`() = runTest {
|
|
||||||
// Arrange
|
|
||||||
every { userWallet.hasDerivation(any(), any()) } returns true
|
|
||||||
|
|
||||||
// Act
|
|
||||||
fetcher.invoke(VirtualAccountStatusFetcher.Params(userWalletId))
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
coVerify(exactly = 1) { singleNetworkStatusFetcher(any()) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.tangem.data.virtualaccount.repository
|
||||||
|
|
||||||
|
import arrow.core.left
|
||||||
|
import arrow.core.right
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.tangem.common.extensions.ByteArrayKey
|
||||||
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
|
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||||
|
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||||
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
internal class DefaultVirtualAccountActivationRepositoryTest {
|
||||||
|
|
||||||
|
private val authDataSource: TangemPayAuthDataSource = mockk()
|
||||||
|
private val derivationsRepository: DerivationsRepository = mockk(relaxUnitFun = true)
|
||||||
|
private val userWalletsListRepository: UserWalletsListRepository = mockk()
|
||||||
|
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||||
|
|
||||||
|
private val repository = DefaultVirtualAccountActivationRepository(
|
||||||
|
authDataSource = authDataSource,
|
||||||
|
derivationsRepository = derivationsRepository,
|
||||||
|
userWalletsListRepository = userWalletsListRepository,
|
||||||
|
dispatchers = dispatchers,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val userWalletId = UserWalletId("011")
|
||||||
|
private val userWallet: UserWallet = mockk { every { walletId } returns userWalletId }
|
||||||
|
|
||||||
|
private val derivedKeys: Map<ByteArrayKey, ExtendedPublicKeysMap> = mapOf(
|
||||||
|
ByteArrayKey(byteArrayOf(1, 2, 3)) to ExtendedPublicKeysMap(emptyMap()),
|
||||||
|
)
|
||||||
|
private val activationData = VirtualAccountActivationData(address = "0xVA", derivedKeys = derivedKeys)
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setUp() {
|
||||||
|
clearMocks(authDataSource, derivationsRepository, userWalletsListRepository)
|
||||||
|
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN datasource returns data WHEN activate THEN derived keys persisted`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
coEvery { authDataSource.produceVirtualAccountData(userWallet) } returns activationData.right()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
repository.activateVirtualAccount(userWalletId)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerify(exactly = 1) { derivationsRepository.storeDerivedKeys(userWalletId, derivedKeys) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN datasource returns error WHEN activate THEN throws AND nothing persisted`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
coEvery { authDataSource.produceVirtualAccountData(userWallet) } returns IllegalStateException("nope").left()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val error = runCatching { repository.activateVirtualAccount(userWalletId) }.exceptionOrNull()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(error).isInstanceOf(IllegalStateException::class.java)
|
||||||
|
coVerify(exactly = 0) { derivationsRepository.storeDerivedKeys(any(), any()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -100,6 +100,11 @@ internal class DefaultColdMapDerivationsRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mergeDerivedKeys(
|
||||||
|
userWallet: UserWallet.Cold,
|
||||||
|
keys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
|
||||||
|
): UserWallet.Cold = userWallet.updateDerivedKeys(keys)
|
||||||
|
|
||||||
override suspend fun hasMissedDerivations(
|
override suspend fun hasMissedDerivations(
|
||||||
userWallet: UserWallet.Cold,
|
userWallet: UserWallet.Cold,
|
||||||
networksWithDerivationPath: Map<BackendId, String?>,
|
networksWithDerivationPath: Map<BackendId, String?>,
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,24 @@ internal class DefaultDerivationsRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun storeDerivedKeys(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
derivedKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
|
||||||
|
) {
|
||||||
|
if (derivedKeys.isEmpty()) {
|
||||||
|
TangemLogger.d("Nothing to store")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
||||||
|
val updatedUserWallet = when (userWallet) {
|
||||||
|
is UserWallet.Cold -> coldDerivationsRepository.mergeDerivedKeys(userWallet, derivedKeys)
|
||||||
|
is UserWallet.Hot -> hotDerivationsRepository.mergeDerivedKeys(userWallet, derivedKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
userWallet.update(updatedUserWallet)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getExistingDerivedKeys(
|
override suspend fun getExistingDerivedKeys(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
seedKey: ByteArrayKey,
|
seedKey: ByteArrayKey,
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,11 @@ internal class DefaultHotMapDerivationsRepository @Inject constructor(
|
||||||
return updatedUserWallet.updateWithNewKeys(newKeys) to newKeys
|
return updatedUserWallet.updateWithNewKeys(newKeys) to newKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mergeDerivedKeys(
|
||||||
|
userWallet: UserWallet.Hot,
|
||||||
|
keys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
|
||||||
|
): UserWallet.Hot = userWallet.updateWithNewKeys(keys)
|
||||||
|
|
||||||
override suspend fun hasMissedDerivations(
|
override suspend fun hasMissedDerivations(
|
||||||
userWallet: UserWallet.Hot,
|
userWallet: UserWallet.Hot,
|
||||||
networksWithDerivationPath: Map<BackendId, String?>,
|
networksWithDerivationPath: Map<BackendId, String?>,
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,7 @@ dependencies {
|
||||||
|
|
||||||
/** Domain models */
|
/** Domain models */
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
|
|
||||||
|
/** Tangem libraries (derived public keys types for VA activation) */
|
||||||
|
implementation(tangemDeps.card.core)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.tangem.domain.visa.model
|
||||||
|
|
||||||
|
import com.tangem.common.extensions.ByteArrayKey
|
||||||
|
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of deriving the Virtual Account key on the card.
|
||||||
|
*
|
||||||
|
* @property address the VA deposit address generated from the derived key
|
||||||
|
* @property derivedKeys the derived extended public key(s) keyed by the seed wallet public key,
|
||||||
|
* ready to be persisted into the wallet (see `DerivationsRepository.storeDerivedKeys`)
|
||||||
|
*/
|
||||||
|
data class VirtualAccountActivationData(
|
||||||
|
val address: String,
|
||||||
|
val derivedKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>,
|
||||||
|
)
|
||||||
|
|
@ -4,11 +4,14 @@ import arrow.core.Either
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.pay.WithdrawalSignatureResult
|
import com.tangem.domain.pay.WithdrawalSignatureResult
|
||||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
|
|
||||||
interface TangemPayAuthDataSource {
|
interface TangemPayAuthDataSource {
|
||||||
|
|
||||||
suspend fun produceInitialCredentials(userWallet: UserWallet): Either<Throwable, TangemPayInitialCredentials>
|
suspend fun produceInitialCredentials(userWallet: UserWallet): Either<Throwable, TangemPayInitialCredentials>
|
||||||
|
|
||||||
|
suspend fun produceVirtualAccountData(userWallet: UserWallet): Either<Throwable, VirtualAccountActivationData>
|
||||||
|
|
||||||
suspend fun getWithdrawalSignature(
|
suspend fun getWithdrawalSignature(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
hash: String,
|
hash: String,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.tangem.domain.virtualaccount.repository
|
||||||
|
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
|
interface VirtualAccountActivationRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the Virtual Account key on the card (NFC) and persists it into the wallet, so the
|
||||||
|
* on-chain VA balance can later be fetched without re-deriving. Throws on failure.
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
suspend fun activateVirtualAccount(userWalletId: UserWalletId)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.domain.virtualaccount.usecase
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.Either.Companion.catch
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.virtualaccount.repository.VirtualAccountActivationRepository
|
||||||
|
|
||||||
|
class ActivateVirtualAccountUseCase(
|
||||||
|
private val repository: VirtualAccountActivationRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId): Either<Throwable, Unit> {
|
||||||
|
return catch {
|
||||||
|
repository.activateVirtualAccount(userWalletId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,9 @@ interface ColdMapDerivationsRepository {
|
||||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||||
): Pair<UserWallet.Cold, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
): Pair<UserWallet.Cold, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
||||||
|
|
||||||
|
/** Merges already-derived [keys] into [userWallet]'s stored derivations without deriving on the card. */
|
||||||
|
fun mergeDerivedKeys(userWallet: UserWallet.Cold, keys: Map<ByteArrayKey, ExtendedPublicKeysMap>): UserWallet.Cold
|
||||||
|
|
||||||
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
||||||
suspend fun hasMissedDerivations(
|
suspend fun hasMissedDerivations(
|
||||||
userWallet: UserWallet.Cold,
|
userWallet: UserWallet.Cold,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,14 @@ interface DerivationsRepository {
|
||||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||||
): Map<ByteArrayKey, ExtendedPublicKeysMap>
|
): Map<ByteArrayKey, ExtendedPublicKeysMap>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges already-derived [derivedKeys] into the wallet's stored derivations and persists it.
|
||||||
|
* Does NOT derive on the card (no NFC): use it to save a key that was obtained by a dedicated
|
||||||
|
* card task. Keyed by the seed wallet public key ([ByteArrayKey]).
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
suspend fun storeDerivedKeys(userWalletId: UserWalletId, derivedKeys: Map<ByteArrayKey, ExtendedPublicKeysMap>)
|
||||||
|
|
||||||
/** Returns already derived extended public keys for the given [seedKey] */
|
/** Returns already derived extended public keys for the given [seedKey] */
|
||||||
suspend fun getExistingDerivedKeys(userWalletId: UserWalletId, seedKey: ByteArrayKey): ExtendedPublicKeysMap
|
suspend fun getExistingDerivedKeys(userWalletId: UserWalletId, seedKey: ByteArrayKey): ExtendedPublicKeysMap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ interface HotMapDerivationsRepository {
|
||||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||||
): Pair<UserWallet.Hot, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
): Pair<UserWallet.Hot, Map<ByteArrayKey, ExtendedPublicKeysMap>>
|
||||||
|
|
||||||
|
/** Merges already-derived [keys] into [userWallet]'s stored derivations. */
|
||||||
|
fun mergeDerivedKeys(userWallet: UserWallet.Hot, keys: Map<ByteArrayKey, ExtendedPublicKeysMap>): UserWallet.Hot
|
||||||
|
|
||||||
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
/** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */
|
||||||
suspend fun hasMissedDerivations(
|
suspend fun hasMissedDerivations(
|
||||||
userWallet: UserWallet.Hot,
|
userWallet: UserWallet.Hot,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.WithdrawalSignatureResult
|
import com.tangem.domain.pay.WithdrawalSignatureResult
|
||||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||||
|
import com.tangem.domain.visa.model.VirtualAccountActivationData
|
||||||
import com.tangem.domain.visa.model.VisaActivationInput
|
import com.tangem.domain.visa.model.VisaActivationInput
|
||||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||||
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
||||||
|
|
@ -175,6 +176,10 @@ interface TangemSdkManager {
|
||||||
preflightReadFilter: PreflightReadFilter,
|
preflightReadFilter: PreflightReadFilter,
|
||||||
): Either<Throwable, TangemPayInitialCredentials>
|
): Either<Throwable, TangemPayInitialCredentials>
|
||||||
|
|
||||||
|
suspend fun tangemPayProduceVirtualAccountData(
|
||||||
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
): Either<Throwable, VirtualAccountActivationData>
|
||||||
|
|
||||||
suspend fun getWithdrawalSignature(
|
suspend fun getWithdrawalSignature(
|
||||||
hash: String,
|
hash: String,
|
||||||
preflightReadFilter: PreflightReadFilter,
|
preflightReadFilter: PreflightReadFilter,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue