Updated on 2026-08-14
This commit is contained in:
commit
e8ccd67098
295 changed files with 4414 additions and 2971 deletions
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
|
|
@ -12,14 +10,11 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
|
|
@ -32,29 +27,12 @@ class TapWalletManager(
|
|||
private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(),
|
||||
) {
|
||||
|
||||
private val blockchainSdkConfig by lazy {
|
||||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
private var loadUserWalletDataJob: Job? = null
|
||||
set(value) {
|
||||
field?.cancel()
|
||||
field = value
|
||||
}
|
||||
|
||||
val walletManagerFactory: WalletManagerFactory by lazy {
|
||||
WalletManagerFactory(
|
||||
config = blockchainSdkConfig,
|
||||
accountCreator = store.inject(DaggerGraphState::accountCreator),
|
||||
blockchainDataStorage = store.inject(DaggerGraphState::blockchainDataStorage),
|
||||
loggers = if (store.inject(DaggerGraphState::feedbackManagerFeatureToggles).isLocalLogsEnabled) {
|
||||
listOf(store.inject(DaggerGraphState::blockchainSDKLogger))
|
||||
} else {
|
||||
emptyList()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) {
|
||||
// If a previous job was running, it gets cancelled before the new one starts,
|
||||
// ensuring that only one job is active at any given time.
|
||||
|
|
@ -79,9 +57,7 @@ class TapWalletManager(
|
|||
// Order is important
|
||||
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions(scanResponse))
|
||||
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.domain.userwallets.UserWalletIdBuilder
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import timber.log.Timber
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard.repository
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.card.repository.ScanCardRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.utils.ScanCardExceptionConverter
|
||||
|
||||
// TODO: Move to the :data:card module
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
package com.tangem.tap.domain.sdk
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
interface TangemSdkManager {
|
||||
|
||||
val canUseBiometry: Boolean
|
||||
|
||||
val needEnrollBiometrics: Boolean
|
||||
|
||||
val keystoreManager: KeystoreManager
|
||||
|
||||
val secureStorage: SecureStorage
|
||||
|
||||
val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
|
||||
suspend fun scanProduct(
|
||||
cardId: String? = null,
|
||||
messageRes: Int? = null,
|
||||
allowsRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<ScanResponse>
|
||||
|
||||
suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean = false,
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse>
|
||||
|
||||
suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey>
|
||||
|
||||
suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit>
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
allowRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
accessCode: String? = null,
|
||||
@DrawableRes iconScanRes: Int? = null,
|
||||
): CompletionResult<T>
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?)
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String
|
||||
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.domain
|
||||
package com.tangem.tap.domain.sdk.impl
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
|
|
@ -29,6 +29,7 @@ import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
|||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
|
|
@ -40,10 +41,10 @@ import kotlinx.coroutines.withContext
|
|||
import kotlin.coroutines.resume
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class TangemSdkManager(
|
||||
class DefaultTangemSdkManager(
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val resources: Resources,
|
||||
) {
|
||||
) : TangemSdkManager {
|
||||
|
||||
private val tangemSdk: TangemSdk
|
||||
get() = cardSdkConfigRepository.sdk
|
||||
|
|
@ -55,25 +56,25 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
val canUseBiometry: Boolean
|
||||
override val canUseBiometry: Boolean
|
||||
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
|
||||
|
||||
val needEnrollBiometrics: Boolean
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
val keystoreManager: KeystoreManager
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = tangemSdk.keystoreManager
|
||||
|
||||
val secureStorage: SecureStorage
|
||||
override val secureStorage: SecureStorage
|
||||
get() = tangemSdk.secureStorage
|
||||
|
||||
val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = tangemSdk.config.userCodeRequestPolicy
|
||||
|
||||
suspend fun scanProduct(
|
||||
cardId: String? = null,
|
||||
messageRes: Int? = null,
|
||||
allowsRequestAccessCodeFromRepository: Boolean = false,
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(
|
||||
|
|
@ -87,9 +88,9 @@ class TangemSdkManager(
|
|||
).also { sendScanResultsToAnalytics(it) }
|
||||
}
|
||||
|
||||
suspend fun createProductWallet(
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean = false,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = CreateProductWalletTask(
|
||||
|
|
@ -103,7 +104,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun importWallet(
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
|
|
@ -135,14 +136,14 @@ class TangemSdkManager(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
|
||||
}
|
||||
|
||||
suspend fun deriveExtendedPublicKey(
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
|
|
@ -153,7 +154,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun resetToFactorySettings(
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
|
|
@ -167,7 +168,7 @@ class TangemSdkManager(
|
|||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.save(
|
||||
cardsIds = cardsIds,
|
||||
userCode = UserCode(
|
||||
|
|
@ -177,15 +178,15 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.delete(cardsIds.toSet())
|
||||
}
|
||||
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
return userCodeRepository.clear()
|
||||
}
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
cardId,
|
||||
|
|
@ -193,7 +194,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changeAccessCode(null),
|
||||
cardId,
|
||||
|
|
@ -201,7 +202,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.resetUserCodes(),
|
||||
cardId,
|
||||
|
|
@ -209,7 +210,10 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeRecoveryAllowedTask(enabled),
|
||||
cardId,
|
||||
|
|
@ -217,9 +221,9 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
allowRequestAccessCodeFromRepository: Boolean = false,
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
||||
|
|
@ -229,12 +233,12 @@ class TangemSdkManager(
|
|||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun <T> runTaskAsync(
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
accessCode: String? = null,
|
||||
@DrawableRes iconScanRes: Int? = null,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage, accessCode, iconScanRes) { result ->
|
||||
|
|
@ -253,7 +257,7 @@ class TangemSdkManager(
|
|||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
tangemSdk.config.cardIdDisplayFormat = when {
|
||||
scanResponse == null -> CardIdDisplayFormat.Full
|
||||
scanResponse.cardTypesResolver.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
|
||||
|
|
@ -262,11 +266,11 @@ class TangemSdkManager(
|
|||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
tangemSdk.config.userCodeRequestPolicy = policy
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
package com.tangem.tap.domain.sdk.impl
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class MockTangemSdkManager(
|
||||
private val resources: Resources,
|
||||
) : TangemSdkManager {
|
||||
|
||||
override val canUseBiometry: Boolean
|
||||
get() = false
|
||||
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = TODO()
|
||||
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = TODO()
|
||||
|
||||
override val secureStorage: SecureStorage
|
||||
get() = TODO()
|
||||
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = TODO()
|
||||
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
return CompletionResult.Success(MockProvider.getScanResponse())
|
||||
}
|
||||
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.tap.domain.sdk.mocks.wallet.WalletMocks
|
||||
import com.tangem.tap.domain.sdk.mocks.wallet2.Wallet2Mocks
|
||||
|
||||
object MockProvider {
|
||||
|
||||
var productType: ProductType = ProductType.Wallet
|
||||
|
||||
fun getScanResponse() = getMocks(productType).scanResponse
|
||||
|
||||
private fun getMocks(productType: ProductType): Mocks {
|
||||
return when (productType) {
|
||||
ProductType.Wallet -> WalletMocks
|
||||
ProductType.Wallet2 -> Wallet2Mocks
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
interface Mocks {
|
||||
|
||||
val scanResponse: ScanResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.wallet
|
||||
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.EncryptionMode
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.tap.domain.sdk.mocks.Mocks
|
||||
import java.util.Date
|
||||
|
||||
object WalletMocks : Mocks {
|
||||
|
||||
private val cardDto = CardDTO(
|
||||
cardId = "AC05000000086747",
|
||||
batchId = "AC05",
|
||||
cardPublicKey = byteArrayOf(2, -120, -3, -32, -122, -127, -120, -104, 59, 72, 76, 114, 94, 75, -37, -55, 55, 99, 66, 123, 85, -87, 80, 106, 105, -116, 87, -83, -12, 70, 108, -68, -39),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 4,
|
||||
minor = 52,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1649635200000),
|
||||
signature = byteArrayOf(-20, 98, -101, 94, -23, 73, 122, -21, 74, 76, 79, -55, -102, -62, -30, 44, -38, 118, 75, 121, -36, 118, -62, 60, -38, -63, 33, -14, -98, -69, 112, 22, 48, -43, 47, 65, -61, -56, 38, -94, -45, 44, 95, -22, 6, -31, -40, -25, 42, 38, 94, -99, 71, 98, -33, 27, -102, 30, 52, -7, 51, -27, 84, 66),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM AG",
|
||||
publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = false,
|
||||
isSettingPasscodeAllowed = false,
|
||||
isResettingUserCodesAllowed = true,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = false,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = false,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
publicKey = 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),
|
||||
chainCode = byteArrayOf(80, 13, -8, -108, -35, 116, -92, 125, -65, -28, 85, 72, -113, 59, 83, 13, 5, -83, -102, 123, 124, -22, 94, 108, -71, 95, 65, -2, 38, 38, -108, 14),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = false,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
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),
|
||||
),
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
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),
|
||||
),
|
||||
DerivationPath("m/44'/1'/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),
|
||||
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),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = 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),
|
||||
chainCode = byteArrayOf(80, 13, -8, -108, -35, 116, -92, 125, -65, -28, 85, 72, -113, 59, 83, 13, 5, -83, -102, 123, 124, -22, 94, 108, -71, 95, 65, -2, 38, 38, -108, 14),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-65, -53, -62, -12, -57, -32, -38, -9, -128, -52, -83, -61, 73, 39, 41, 15, -74, -97, 38, 52, -101, 63, 74, -56, -20, 15, 57, -127, 114, -93, -17, -109),
|
||||
chainCode = byteArrayOf(-81, 15, 125, 28, -115, -22, 87, 81, 87, -123, -25, -74, 86, 2, 1, 110, -115, 65, -110, 63, -64, 83, -93, -97, -104, 123, 12, -26, 94, 27, 84, -6),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = false,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-65, -53, -62, -12, -57, -32, -38, -9, -128, -52, -83, -61, 73, 39, 41, 15, -74, -97, 38, 52, -101, 63, 74, -56, -20, 15, 57, -127, 114, -93, -17, -109),
|
||||
chainCode = byteArrayOf(-81, 15, 125, 28, -115, -22, 87, 81, 87, -123, -25, -74, 86, 2, 1, 110, -115, 65, -110, 63, -64, 83, -93, -97, -104, 123, 12, -26, 94, 27, 84, -6),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.NoBackup,
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.wallet2
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.Mocks
|
||||
|
||||
object Wallet2Mocks : Mocks {
|
||||
|
||||
override val scanResponse: ScanResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.LegacySettingsRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
|
||||
internal class DefaultLegacySettingsRepository(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package com.tangem.tap.domain.tasks.product
|
|||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.common.KeyPair
|
|||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
|
|
@ -88,6 +88,7 @@ class TwinCardsManager(
|
|||
)
|
||||
}
|
||||
|
||||
@Deprecated(message = "Use AssetReader instead")
|
||||
private fun getIssuers(reader: AssetReader): List<Issuer> {
|
||||
val file = reader.readJson(fileName = "tangem-app-config/issuers")
|
||||
return getAdapter().fromJson(file)!!
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
|
||||
internal class DefaultUserWalletsListManagerFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : UserWalletsListManagerFeatureToggles {
|
||||
|
||||
override val isGeneralManagerEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED")
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList.di
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.tap.domain.userWalletList.DefaultUserWalletsListManagerFeatureToggles
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object UserWalletsListManagerFeatureTogglesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserWalletsListManagerFeatureToggles(
|
||||
featureTogglesManager: FeatureTogglesManager,
|
||||
): UserWalletsListManagerFeatureToggles {
|
||||
return DefaultUserWalletsListManagerFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ internal object UserWalletsListManagerModule {
|
|||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
storageName = "user_wallets_storage",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList.di
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.authentication.storage.AuthenticatedStorage
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.createEncryptedSharedPreferences
|
||||
import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.RuntimeUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.DelegatedKeystoreManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysStoreDecorator
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.BiometricUserWalletsKeysRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultSelectedUserWalletRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsPublicInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.utils.json.*
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
|
||||
|
||||
fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
||||
applicationContext: Context,
|
||||
): UserWalletsListManager {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(WalletDerivedKeysMapAdapter())
|
||||
.add(ScanResponseDerivedKeysMapAdapter())
|
||||
.add(ByteArrayKeyAdapter())
|
||||
.add(ExtendedPublicKeysMapAdapter())
|
||||
.add(CardBackupStatusAdapter())
|
||||
.add(DerivationPathAdapterWithMigration())
|
||||
.add(TangemSdkAdapter.DateAdapter())
|
||||
.add(TangemSdkAdapter.DerivationNodeAdapter())
|
||||
.add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
val authenticatedStorage = AuthenticatedStorage(
|
||||
secureStorage = UserWalletsKeysStoreDecorator(
|
||||
featureStorage = secureStorage,
|
||||
cardSdkStorageProvider = Provider { tangemSdkManager.secureStorage },
|
||||
),
|
||||
keystoreManager = DelegatedKeystoreManager(
|
||||
keystoreManagerProvider = Provider { tangemSdkManager.keystoreManager },
|
||||
),
|
||||
)
|
||||
|
||||
val keysRepository = BiometricUserWalletsKeysRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
authenticatedStorage = authenticatedStorage,
|
||||
)
|
||||
val publicInformationRepository = DefaultUserWalletsPublicInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
val sensitiveInformationRepository = DefaultUserWalletsSensitiveInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
val selectedUserWalletRepository = DefaultSelectedUserWalletRepository(
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
|
||||
return BiometricUserWalletsListManager(
|
||||
keysRepository = keysRepository,
|
||||
publicInformationRepository = publicInformationRepository,
|
||||
sensitiveInformationRepository = sensitiveInformationRepository,
|
||||
selectedUserWalletRepository = selectedUserWalletRepository,
|
||||
)
|
||||
}
|
||||
|
||||
fun UserWalletsListManager.Companion.provideRuntimeImplementation(): UserWalletsListManager {
|
||||
return RuntimeUserWalletsListManager()
|
||||
}
|
||||
|
|
@ -10,13 +10,11 @@ import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTrans
|
|||
import com.tangem.tap.domain.walletconnect2.domain.models.binance.tradeOrderSerializer
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.TradeData
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import timber.log.Timber
|
||||
|
||||
internal object BnbHelper {
|
||||
|
||||
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
fun createMessageData(order: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
val input = order.msgs.first().inputs.first()
|
||||
val output = order.msgs.first().inputs.first()
|
||||
|
||||
|
|
@ -42,51 +40,7 @@ internal object BnbHelper {
|
|||
)
|
||||
}
|
||||
|
||||
fun WcBinanceTradeOrder.toWCBinanceTradeOrder(): WCBinanceTradeOrder {
|
||||
return WCBinanceTradeOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTradeOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTradeOrder.Message.toWCBinanceTradeOrderMessage(): WCBinanceTradeOrder.Message {
|
||||
return WCBinanceTradeOrder.Message(id, orderType, price, quantity, sender, side, symbol, timeInforce)
|
||||
}
|
||||
|
||||
fun WcBinanceTransferOrder.toWCBinanceTransferOrder(): WCBinanceTransferOrder {
|
||||
return WCBinanceTransferOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTransferOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.toWCBinanceTransferOrderMessage(): WCBinanceTransferOrder.Message {
|
||||
return WCBinanceTransferOrder.Message(
|
||||
inputs.map { it.toWCBinanceItem() },
|
||||
outputs.map { it.toWCBinanceItem() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.Item.toWCBinanceItem(): WCBinanceTransferOrder.Message.Item {
|
||||
return WCBinanceTransferOrder.Message.Item(
|
||||
address,
|
||||
coins.map {
|
||||
WCBinanceTransferOrder.Message.Item.Coin(it.amount, it.denom)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
fun createMessageData(order: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
val address = order.msgs.first().sender
|
||||
|
||||
val tradeData = order.msgs.map {
|
||||
|
|
|
|||
|
|
@ -1,562 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.walletconnect.extensions.isDappSupported
|
||||
import com.tangem.tap.domain.walletconnect.extensions.toWcEthereumSignMessage
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.walletConnectRepository
|
||||
import com.trustwallet.walletconnect.WCClient
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCAddNetwork
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import com.trustwallet.walletconnect.models.session.WCSessionUpdate
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
@Suppress("LargeClass")
|
||||
internal class WalletConnectManager {
|
||||
|
||||
private var cardId: String? = null
|
||||
|
||||
private val okHttpClient: OkHttpClient by lazy {
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.addInterceptor(interceptor)
|
||||
.addInterceptor(RetryInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val interceptor by lazy {
|
||||
createNetworkLoggingInterceptor()
|
||||
}
|
||||
|
||||
private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf()
|
||||
|
||||
fun connect(wcUri: String) {
|
||||
val session = WCSession.from(wcUri).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
if (sessions[session.topic] != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
|
||||
return
|
||||
}
|
||||
val client = WCClient(httpClient = okHttpClient)
|
||||
setListeners(client)
|
||||
val peerId = UUID.randomUUID().toString()
|
||||
|
||||
try {
|
||||
client.connect(session, tangemPeerMeta, peerId)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sessions[session.topic] = WalletConnectActiveData(
|
||||
peerId = peerId,
|
||||
remotePeerId = null,
|
||||
session = session,
|
||||
client = client,
|
||||
wallet = WalletForSession(),
|
||||
)
|
||||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
||||
fun updateSession(session: WalletConnectSession) {
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
fun updateBlockchain(session: WalletConnectSession) {
|
||||
sessions[session.session.topic]?.client?.updateSession(
|
||||
accounts = listOfNotNull(session.getAddress()),
|
||||
chainId = session.wallet.blockchain?.getChainId(),
|
||||
approved = true,
|
||||
)
|
||||
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun setupConnectionTimeoutCheck(session: WCSession) {
|
||||
scope.launch {
|
||||
delay(20_000)
|
||||
val data = sessions[session.topic]
|
||||
if (data != null && data.peerMeta == null) {
|
||||
disconnect(session)
|
||||
store.dispatchOnMain(WalletConnectAction.OpeningSessionTimeout(session))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreSessions(scanResponse: ScanResponse) {
|
||||
val walletPublicKey = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }?.publicKey
|
||||
?: return
|
||||
if (scanResponse.card.backupStatus?.isActive != true) cardId = scanResponse.card.cardId
|
||||
val sessions = walletConnectRepository.loadSavedSessions()
|
||||
// filter sessions for this particular card
|
||||
.filter { it.wallet.walletPublicKey.contentEquals(walletPublicKey) }
|
||||
this.sessions = sessions
|
||||
.map { session ->
|
||||
WalletConnectActiveData(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
client = WCClient(httpClient = okHttpClient),
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
wallet = session.wallet,
|
||||
)
|
||||
.also {
|
||||
setListeners(it.client)
|
||||
it.client.connect(it.session, tangemPeerMeta, it.peerId, it.remotePeerId)
|
||||
}
|
||||
}.associateBy { it.session.topic }.toMutableMap()
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.SetSessionsRestored(sessions))
|
||||
}
|
||||
|
||||
fun approve(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey ?: return
|
||||
val blockchain = activeData.wallet.getBlockchainForSession()
|
||||
val accounts = listOf(blockchain.makeAddresses(key).first().value)
|
||||
val approved = activeData.client.approveSession(
|
||||
accounts = accounts,
|
||||
chainId = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!!,
|
||||
)
|
||||
if (approved) {
|
||||
val walletConnectSession = WalletConnectSession(
|
||||
peerId = activeData.peerId,
|
||||
remotePeerId = activeData.remotePeerId,
|
||||
wallet = activeData.wallet,
|
||||
session = session,
|
||||
peerMeta = activeData.peerMeta!!,
|
||||
)
|
||||
walletConnectRepository.saveSession(walletConnectSession)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ApproveSession.Success(
|
||||
walletConnectSession,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true &&
|
||||
it.value.peerMeta?.url == activeData.peerMeta?.url &&
|
||||
it.value.session != activeData.session
|
||||
}
|
||||
Timber.d("RemoveSimilarSessions: ${sessionsToRemove.values.map { it.client.session }}")
|
||||
sessionsToRemove.forEach { disconnect(it.value.session) }
|
||||
}
|
||||
|
||||
fun rejectRequest(topic: String, id: Long) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.rejectRequest(id)
|
||||
}
|
||||
|
||||
private fun acceptRequest(topic: String, id: Long, data: String) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.approveRequest(id, data)
|
||||
}
|
||||
|
||||
fun disconnect(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
val disconnected = if (activeData.client.isConnected) {
|
||||
activeData.client.killSession()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
if (disconnected) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSessionClosed(session: WCSession) {
|
||||
sessions.remove(session.topic)
|
||||
walletConnectRepository.removeSession(session)
|
||||
store.dispatchOnMain(WalletConnectAction.RemoveSession(session))
|
||||
}
|
||||
|
||||
fun handleTransactionRequest(
|
||||
transaction: WcEthereumTransaction,
|
||||
session: WalletConnectSession,
|
||||
id: Long,
|
||||
type: WcEthTransactionType,
|
||||
) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareTransactionData(
|
||||
EthTransactionData(
|
||||
transaction = transaction,
|
||||
networkId = session.wallet.blockchain?.toNetworkId() ?: "",
|
||||
rawDerivationPath = session.wallet.derivationPath?.rawPath,
|
||||
id = id,
|
||||
topic = session.session.topic,
|
||||
type = type,
|
||||
metaName = session.peerMeta.name,
|
||||
metaUrl = session.peerMeta.url,
|
||||
),
|
||||
).guard {
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
session.session.topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = data)
|
||||
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.RequestTransaction(
|
||||
WcPreparedRequest.EthTransaction(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = data.walletManager.wallet.publicKey.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun completeTransaction(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.transactionData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().completeTransaction(data, cardId).guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun signBnb(id: Long, data: ByteArray, topic: Topic) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signBnbTransaction(
|
||||
data = data,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, id, hash)
|
||||
}
|
||||
}
|
||||
|
||||
fun handlePersonalSignRequest(message: WCEthereumSignMessage, session: WalletConnectSession, id: Long) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareDataForPersonalSign(
|
||||
message = message.toWcEthereumSignMessage(),
|
||||
topic = session.session.topic,
|
||||
id = id,
|
||||
metaName = session.peerMeta.name,
|
||||
)
|
||||
sessions[session.session.topic] = activeData.copy(personalSignData = data)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.PersonalSign(
|
||||
WcPreparedRequest.EthSign(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = session.wallet.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendSignedMessage(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.personalSignData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signPersonalMessage(
|
||||
hashToSign = data.hash,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
type = data.type,
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
)
|
||||
.guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[topic] = activeData.copy(personalSignData = data)
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun setListeners(client: WCClient) {
|
||||
client.onSessionRequest = { id: Long, peer: WCPeerMeta ->
|
||||
Timber.d("OnSessionRequest: $peer")
|
||||
val session = client.session
|
||||
val data = sessions[session?.topic]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId)
|
||||
if (data != null && session != null) {
|
||||
if (!peer.isDappSupported()) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = session,
|
||||
error = TapError.WalletConnect.UnsupportedDapp,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
sessions[session.topic] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client.onSessionUpdate = { id: Long, update: WCSessionUpdate ->
|
||||
Timber.d("onSessionUpdate: $update")
|
||||
val session = client.session
|
||||
if (session != null && !update.approved) onSessionClosed(session)
|
||||
}
|
||||
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSendTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SendTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSendTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSignTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SignTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSignTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
|
||||
Timber.d("onEthSign: $message")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.PersonalSign
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
client.onBnbCancel = { id: Long, order: WCBinanceCancelOrder ->
|
||||
}
|
||||
client.onBnbTrade = { id: Long, order: WCBinanceTradeOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Trade(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTransfer = { id: Long, order: WCBinanceTransferOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Transfer(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTxConfirm = { id: Long, order: WCBinanceTxConfirmParam ->
|
||||
// send empty approve request if status is OK
|
||||
if (order.ok) client.approveRequest(id, "")
|
||||
}
|
||||
client.onDisconnect = { code: Int, reason: String ->
|
||||
val session = client.session
|
||||
if (session != null) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
client.onWalletChangeNetwork = { id: Long, chainId: Int ->
|
||||
switchChain(chainId, client)
|
||||
}
|
||||
client.onWalletAddNetwork = { id: Long, network: WCAddNetwork ->
|
||||
// TODO
|
||||
// In fact this method is used to add a EVM network. It provides RPC url and chain ID.
|
||||
// Here now it just tries to switch to a EVM network with a provided chain ID.
|
||||
try {
|
||||
val chainId = Integer.decode(network.chainIdHex)
|
||||
switchChain(chainId, client)
|
||||
} catch (exception: Exception) {
|
||||
Timber.d("WC add network error: chain could not be parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchChain(chainId: Int, client: WCClient) {
|
||||
val blockchain = Blockchain.fromChainId(chainId)
|
||||
Timber.d("WC switch chainID\nNew Blockchain: $blockchain")
|
||||
val session = sessions[client.session?.topic]?.toWalletConnectSession()
|
||||
if (session != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.SwitchBlockchain(blockchain, session))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WC_SCHEME = "wc"
|
||||
|
||||
private val tangemPeerMeta = WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com")
|
||||
|
||||
fun isCorrectWcUri(string: String): Boolean = WCSession.from(string) != null
|
||||
}
|
||||
}
|
||||
|
||||
internal typealias Topic = String
|
||||
|
||||
internal data class WalletConnectActiveData(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val client: WCClient,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta? = null,
|
||||
val wallet: WalletForSession,
|
||||
val transactionData: WcTransactionData? = null,
|
||||
val personalSignData: WcPersonalSignData? = null,
|
||||
) {
|
||||
fun toWalletConnectSession(): WalletConnectSession? {
|
||||
if (peerMeta == null) return null
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal class RetryInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request: Request = chain.request()
|
||||
val response = chain.proceed(request)
|
||||
when (response.code) {
|
||||
502 -> {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
object WalletConnectNetworkUtils {
|
||||
fun parseBlockchain(chainId: Int?, peer: WCPeerMeta): Blockchain? {
|
||||
return when {
|
||||
peer.url.contains("pancakeswap.finance") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
peer.url.contains("optimism") -> {
|
||||
Blockchain.Optimism
|
||||
}
|
||||
chainId != null -> {
|
||||
Blockchain.fromChainId(chainId)
|
||||
}
|
||||
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
|
||||
Blockchain.Polygon
|
||||
}
|
||||
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
|
||||
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
|
||||
Blockchain.BinanceTestnet
|
||||
} else {
|
||||
Blockchain.Binance
|
||||
}
|
||||
}
|
||||
peer.name.contains("BSC") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
peer.url.contains("honeyswap.1hive.eth.limo") -> {
|
||||
// Check if something's changed after this bug report:
|
||||
// https://github.com/1Hive/honeyswap-interface/issues/83
|
||||
Blockchain.Gnosis
|
||||
}
|
||||
else -> {
|
||||
Blockchain.Ethereum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import timber.log.Timber
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class WalletConnectRepository(val context: Application) {
|
||||
private val walletConnectAdapter: JsonAdapter<List<SessionDao>> = MoshiConverter.sdkMoshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, SessionDao::class.java),
|
||||
)
|
||||
|
||||
fun saveSession(session: WalletConnectSession) {
|
||||
val sessions = loadSavedSessions() + session
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun removeSession(session: WCSession) {
|
||||
val sessions = loadSavedSessions().filterNot { it.session == session }
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun loadSavedSessions(): List<WalletConnectSession> {
|
||||
return try {
|
||||
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
|
||||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (e: FileNotFoundException) {
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Timber.w(exception)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveSessions(sessions: List<WalletConnectSession>) {
|
||||
val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) })
|
||||
.utf8ToHex() // convert to hex to solve problems with saving text with emojis
|
||||
Timber.e("WC sessions, saving following json: $json")
|
||||
context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS)
|
||||
}
|
||||
|
||||
private fun String.utf8ToHex(): String {
|
||||
return this.toByteArray().toHexString()
|
||||
}
|
||||
|
||||
private fun String.hexToUtf8(): String {
|
||||
return this.hexToBytes().toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
private fun Context.readFileText(fileName: String): String =
|
||||
this.openFileInput(fileName).bufferedReader().readText()
|
||||
|
||||
private fun Context.rewriteFile(content: String, fileName: String) {
|
||||
this.openFileOutput(fileName, Context.MODE_PRIVATE).use {
|
||||
it.write(content.toByteArray(), 0, content.length)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FILE_NAME_PREFIX_SESSIONS = "wc_sessions"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SessionDao(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
) {
|
||||
fun toSession(): WalletConnectSession {
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): SessionDao {
|
||||
return SessionDao(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
wallet = session.wallet,
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.toKeccak
|
|||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.*
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toDecompressedPublicKey
|
||||
|
|
@ -15,13 +16,10 @@ import com.tangem.common.extensions.toHexString
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.Basic.TransactionSent.MemoType
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTradeOrder
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTransferOrder
|
||||
import com.tangem.tap.domain.walletconnect2.domain.TransactionType
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
|
|
@ -227,11 +225,11 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
|
||||
fun prepareBnbTradeOrder(data: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTradeOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
fun prepareBnbTransferOrder(data: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTransferOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
suspend fun signBnbTransaction(
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect.extensions
|
||||
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
|
||||
internal fun WCPeerMeta.isDappSupported(): Boolean {
|
||||
return !unsupportedDappsList.any { this.url.contains(it) }
|
||||
}
|
||||
|
||||
private val unsupportedDappsList: List<String> = listOf("dydx.exchange")
|
||||
|
||||
internal fun WCEthereumTransaction.toWcEthTransaction(): WcEthereumTransaction {
|
||||
return WcEthereumTransaction(
|
||||
from = from,
|
||||
to = to,
|
||||
nonce = nonce,
|
||||
gasPrice = gasPrice,
|
||||
maxFeePerGas = maxFeePerGas,
|
||||
maxPriorityFeePerGas = maxPriorityFeePerGas,
|
||||
gas = gas,
|
||||
gasLimit = gasLimit,
|
||||
value = value,
|
||||
data = data,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun WCEthereumSignMessage.toWcEthereumSignMessage(): WcSignMessage {
|
||||
return WcSignMessage(
|
||||
raw = raw,
|
||||
type = when (type) {
|
||||
WCEthereumSignMessage.WCSignType.MESSAGE -> WcSignMessage.WCSignType.MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE -> WcSignMessage.WCSignType.PERSONAL_MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.TYPED_MESSAGE -> WcSignMessage.WCSignType.TYPED_MESSAGE
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.tap.domain.walletconnect2.app
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
|
||||
internal class TangemWcBlockchainHelper(
|
||||
private val featureToggles: WalletConnectFeatureToggles,
|
||||
featureToggles: WalletConnectFeatureToggles,
|
||||
) : WcBlockchainHelper {
|
||||
|
||||
private val supportedNonEvmBlockchains = if (featureToggles.isSolanaTxSignEnabled) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.di.SdkMoshi
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.app.WalletConnectEventsHandlerImpl
|
||||
|
|
@ -35,6 +38,9 @@ internal object WalletConnectInteractorModule {
|
|||
wcRepository: WalletConnectRepository,
|
||||
wcSessionsRepository: WalletConnectSessionsRepository,
|
||||
walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
walletsStateHolder: WalletsStateHolder,
|
||||
): WalletConnectInteractor {
|
||||
return WalletConnectInteractor(
|
||||
handler = WalletConnectEventsHandlerImpl(),
|
||||
|
|
@ -42,7 +48,10 @@ internal object WalletConnectInteractorModule {
|
|||
sessionsRepository = wcSessionsRepository,
|
||||
sdkHelper = WalletConnectSdkHelper(),
|
||||
blockchainHelper = TangemWcBlockchainHelper(walletConnectFeatureToggles),
|
||||
dispatcher = AppCoroutineDispatcherProvider(),
|
||||
currenciesRepository = currenciesRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
walletsStateHolder = walletsStateHolder,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,52 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
class WalletConnectInteractor(
|
||||
private val handler: WalletConnectEventsHandler,
|
||||
private val walletConnectRepository: WalletConnectRepository,
|
||||
private val sessionsRepository: WalletConnectSessionsRepository,
|
||||
private val sdkHelper: WalletConnectSdkHelper,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val walletsStateHolder: WalletsStateHolder,
|
||||
val blockchainHelper: WcBlockchainHelper,
|
||||
) {
|
||||
|
||||
private val getSelectedWalletUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
GetSelectedWalletUseCase(walletsStateHolder)
|
||||
}
|
||||
|
||||
private val wcScope = CoroutineScope(
|
||||
Job() + dispatchers.io + FeatureCoroutineExceptionHandler.create("wcScope"),
|
||||
)
|
||||
|
||||
private val listenerScope = CoroutineScope(
|
||||
Job() + dispatchers.io + FeatureCoroutineExceptionHandler.create("listenScope"),
|
||||
)
|
||||
|
||||
private val events = walletConnectRepository.events
|
||||
private val sessions = walletConnectRepository.activeSessions
|
||||
|
||||
|
|
@ -34,19 +60,56 @@ class WalletConnectInteractor(
|
|||
sdkHelper = sdkHelper,
|
||||
)
|
||||
|
||||
suspend fun startListening(userWalletId: String, cardId: String?) {
|
||||
init {
|
||||
getSelectedWalletUseCase().onRight { userWalletFlow ->
|
||||
userWalletFlow
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach(::initWithWallet)
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(wcScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initWithWallet(userWallet: UserWallet) {
|
||||
if (userWallet.isMultiCurrency) {
|
||||
Timber.d("WalletConnect: initialize and setup networks for ${userWallet.walletId}")
|
||||
startListeningWc(userWallet.walletId.stringValue, getCardId(userWallet))
|
||||
subscribeOnCurrenciesUpdates(userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrenciesUpdates(userWallet: UserWallet) {
|
||||
currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWallet.walletId)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { currencies ->
|
||||
setupUserChains(userWallet, currencies)
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(wcScope)
|
||||
}
|
||||
|
||||
private suspend fun setupUserChains(userWallet: UserWallet, currencies: List<CryptoCurrency>) {
|
||||
val accounts = getAccountsForWc(
|
||||
userWallet = userWallet,
|
||||
networks = currencies.map { it.network },
|
||||
)
|
||||
setUserChains(accounts)
|
||||
}
|
||||
|
||||
private suspend fun startListeningWc(userWalletId: String, cardId: String?) {
|
||||
this.userWalletId = userWalletId
|
||||
this.cardId = cardId
|
||||
|
||||
coroutineScope {
|
||||
listenerScope.coroutineContext.cancelChildren()
|
||||
listenerScope.launch {
|
||||
launch { subscribeToEvents() }
|
||||
launch { subscribeToSessions() }
|
||||
|
||||
walletConnectRepository.updateSessions()
|
||||
}
|
||||
}
|
||||
|
||||
fun setUserChains(accounts: List<Account>) {
|
||||
private fun setUserChains(accounts: List<Account>) {
|
||||
val userNamespaces: Map<NetworkNamespace, List<Account>> = accounts
|
||||
.groupBy { account ->
|
||||
blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId)
|
||||
|
|
@ -106,7 +169,7 @@ class WalletConnectInteractor(
|
|||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatcher.io)
|
||||
.flowOn(dispatchers.io)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +180,7 @@ class WalletConnectInteractor(
|
|||
val filteredSessions = filterSessionsForUserWallet(listOfSessions, relevantTopics)
|
||||
handler.onListOfSessionsUpdated(filteredSessions)
|
||||
}
|
||||
.flowOn(dispatcher.io)
|
||||
.flowOn(dispatchers.io)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
@ -258,6 +321,38 @@ class WalletConnectInteractor(
|
|||
return uri.lowercase().startsWith(WC_SCHEME)
|
||||
}
|
||||
|
||||
private fun getCardId(userWallet: UserWallet): String? {
|
||||
return if (userWallet.scanResponse.card.backupStatus?.isActive != true) {
|
||||
userWallet.cardId
|
||||
} else { // if wallet has backup, any card from wallet can be used to sign
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getAccountsForWc(userWallet: UserWallet, networks: List<Network>): List<Account> {
|
||||
val walletManagers = networks.mapNotNull {
|
||||
val blockchain = Blockchain.fromId(it.id.value)
|
||||
walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWallet.walletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = it.derivationPath.value,
|
||||
)
|
||||
}
|
||||
return walletManagers.mapNotNull {
|
||||
val wallet = it.wallet
|
||||
val chainId = blockchainHelper.networkIdToChainIdOrNull(
|
||||
wallet.blockchain.toNetworkId(),
|
||||
)
|
||||
chainId?.let {
|
||||
Account(
|
||||
chainId,
|
||||
wallet.address,
|
||||
wallet.publicKey.derivationPath?.rawPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun prepareRequestData(sessionRequest: WalletConnectEvents.SessionRequest): WcPreparedRequest? {
|
||||
return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue