Updated on 2026-08-14
This commit is contained in:
parent
49904463e0
commit
28d8d33ace
20 changed files with 428 additions and 84 deletions
|
|
@ -5,9 +5,9 @@ import com.tangem.blockchain.common.BlockchainSdkError
|
|||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.common.TestActions
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.walletmanager.DefaultWalletManagersFacade
|
||||
import com.tangem.domain.walletmanager.WalletManagerFacade
|
||||
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 WalletManagersFacadeModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWalletManagersFacade(
|
||||
walletManagersStore: WalletManagersStore,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
demoConfig: DemoConfig,
|
||||
configManager: ConfigManager,
|
||||
): WalletManagerFacade {
|
||||
return DefaultWalletManagersFacade(walletManagersStore, userWalletsStore, demoConfig, configManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
|
||||
return when (this) {
|
||||
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE
|
||||
Blockchain.XRP -> BigDecimal(10)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.minimalAmount(): BigDecimal {
|
||||
return 1.toBigDecimal().movePointLeft(decimals())
|
||||
}
|
||||
|
||||
fun Blockchain.getPrimaryCurve(): EllipticCurve? {
|
||||
return when {
|
||||
getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
EllipticCurve.Secp256k1
|
||||
}
|
||||
getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val NODL = "NODL"
|
||||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
|||
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.TwinsHelper
|
||||
import com.tangem.domain.common.extensions.getPrimaryCurve
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
|
@ -30,7 +31,6 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
|||
import com.tangem.operations.files.ReadFilesTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.extensions.getPrimaryCurve
|
||||
import com.tangem.tap.domain.tokens.UserTokensRepository
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ import com.tangem.crypto.hdWallet.DerivationPath
|
|||
import com.tangem.domain.common.BlockchainNetwork
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.extensions.makeWalletManagerForApp
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.legacy.WalletManagersRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresError
|
||||
import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.blockchain.common.BlockchainSdkError
|
|||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.tap.domain.extensions.amountToCreateAccount
|
||||
import com.tangem.domain.common.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.tap.features.demo
|
||||
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.domain.common.extensions.makePrimaryWalletManager
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.makePrimaryWalletManager
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
|
|
@ -14,7 +15,6 @@ import com.tangem.tap.common.redux.AppDialog
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.onboarding.OnboardingDialog
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.makePrimaryWalletManager
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.common.util.twinsIsTwinned
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
|
@ -19,7 +20,6 @@ import com.tangem.tap.common.redux.AppDialog
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.domain.userWalletList.isLockedSync
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.extensions.minimalAmount
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.tap.*
|
||||
|
|
@ -31,7 +32,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
|
|||
import com.tangem.tap.domain.TangemSigner
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.extensions.minimalAmount
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ dependencies {
|
|||
implementation(project(":core:utils"))
|
||||
implementation(project(":common"))
|
||||
implementation(project(":libs:auth"))
|
||||
implementation(project(":domain:models"))
|
||||
implementation(projects.domain.demo)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** Tangem libraries */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.domain.common.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
|
||||
|
|
@ -187,6 +190,35 @@ fun Blockchain.isSupportedInApp(): Boolean {
|
|||
return !excludedBlockchains.contains(this)
|
||||
}
|
||||
|
||||
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
|
||||
return when (this) {
|
||||
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(NODL_AMOUNT_TO_CREATE_ACCOUNT) else BigDecimal.ONE
|
||||
Blockchain.XRP -> BigDecimal.TEN
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.minimalAmount(): BigDecimal {
|
||||
return 1.toBigDecimal().movePointLeft(decimals())
|
||||
}
|
||||
|
||||
fun Blockchain.getPrimaryCurve(): EllipticCurve? {
|
||||
return when {
|
||||
getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
EllipticCurve.Secp256k1
|
||||
}
|
||||
getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val NODL = "NODL"
|
||||
private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
|
||||
|
||||
private val excludedBlockchains = listOf(
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Ducatus,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
package com.tangem.domain.common.extensions
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.BlockchainNetwork
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse,
|
||||
|
|
@ -38,7 +35,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
|||
curve = wallet.curve,
|
||||
)
|
||||
}
|
||||
scanResponse.card.isHdWalletAllowedByApp && seedKey != null && derivationParams != null -> {
|
||||
scanResponse.card.settings.isHDWalletAllowed && seedKey != null && derivationParams != null -> {
|
||||
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
|
||||
val derivationPath = when (derivationParams) {
|
||||
is DerivationParams.Default -> blockchain.derivationPath(derivationParams.style)
|
||||
|
|
@ -64,23 +61,8 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
|||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse,
|
||||
blockchainNetwork: BlockchainNetwork,
|
||||
): WalletManager? {
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationParams = getDerivationParams(blockchainNetwork.derivationPath, scanResponse.card),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDerivationParams(derivationPath: String?, card: CardDTO): DerivationParams? {
|
||||
return derivationPath?.let {
|
||||
DerivationParams.Custom(
|
||||
DerivationPath(it),
|
||||
)
|
||||
} ?: if (!card.settings.isHDWalletAllowed) {
|
||||
private fun getDerivationParams(card: CardDTO): DerivationParams? {
|
||||
return if (!card.settings.isHDWalletAllowed) {
|
||||
null
|
||||
} else if (card.useOldStyleDerivation) {
|
||||
DerivationParams.Default(DerivationStyle.LEGACY)
|
||||
|
|
@ -89,30 +71,13 @@ private fun getDerivationParams(derivationPath: String?, card: CardDTO): Derivat
|
|||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(scanResponse: ScanResponse, currency: Currency): WalletManager? {
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchain = currency.blockchain,
|
||||
derivationParams = getDerivationParams(currency.derivationPath, scanResponse.card),
|
||||
)
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Currency>,
|
||||
): List<WalletManager> {
|
||||
return blockchains
|
||||
.filter { it.isBlockchain() }
|
||||
.mapNotNull { this.makeWalletManagerForApp(scanResponse, it) }
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makePrimaryWalletManager(scanResponse: ScanResponse): WalletManager? {
|
||||
val blockchain = if (scanResponse.card.isTestCard) {
|
||||
scanResponse.cardTypesResolver.getBlockchain().getTestnetVersion() ?: return null
|
||||
} else {
|
||||
scanResponse.cardTypesResolver.getBlockchain()
|
||||
}
|
||||
val derivationParams = getDerivationParams(null, scanResponse.card)
|
||||
val derivationParams = getDerivationParams(scanResponse.card)
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
package com.tangem.domain.walletmanager
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.util.hasDerivation
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.walletmanager.utils.SdkTokenConverter
|
||||
import com.tangem.domain.walletmanager.utils.UpdateWalletManagerResultFactory
|
||||
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import timber.log.Timber
|
||||
|
||||
// FIXME: Move to its own module and make internal
|
||||
@Deprecated("Inject the WalletManagerFacade interface using DI instead")
|
||||
class DefaultWalletManagersFacade(
|
||||
private val walletManagersStore: WalletManagersStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val demoConfig: DemoConfig,
|
||||
configManager: ConfigManager,
|
||||
) : WalletManagerFacade {
|
||||
|
||||
private val resultFactory by lazy { UpdateWalletManagerResultFactory() }
|
||||
private val walletManagerFactory by lazy { WalletManagerFactory(configManager) }
|
||||
private val sdkTokenConverter by lazy { SdkTokenConverter() }
|
||||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
val blockchain = Blockchain.fromId(networkId.value)
|
||||
|
||||
return getAndUpdateWalletManager(userWallet, blockchain, extraTokens)
|
||||
}
|
||||
|
||||
private suspend fun getAndUpdateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val scanResponse = userWallet.scanResponse
|
||||
val derivationPath = blockchain.derivationPath(scanResponse.card.derivationStyle)
|
||||
|
||||
if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath)) {
|
||||
Timber.e("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
||||
val walletManager = getOrCreateWalletManager(userWallet, blockchain, derivationPath)
|
||||
if (walletManager == null || blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to get a wallet manager for blockchain: $blockchain")
|
||||
return UpdateWalletManagerResult.Unreachable
|
||||
}
|
||||
|
||||
updateWalletManagerTokensIfNeeded(walletManager, extraTokens)
|
||||
|
||||
return try {
|
||||
if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
updateDemoWalletManager(walletManager, extraTokens)
|
||||
} else {
|
||||
updateWalletManager(walletManager)
|
||||
}
|
||||
} finally {
|
||||
walletManagersStore.store(userWallet.walletId, walletManager)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDemoWalletManager(
|
||||
walletManager: WalletManager,
|
||||
tokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val amount = demoConfig.getBalance(walletManager.wallet.blockchain)
|
||||
walletManager.wallet.setAmount(amount)
|
||||
|
||||
return resultFactory.getDemoResult(amount, tokens)
|
||||
}
|
||||
|
||||
private suspend fun updateWalletManager(walletManager: WalletManager): UpdateWalletManagerResult {
|
||||
return try {
|
||||
walletManager.update()
|
||||
|
||||
resultFactory.getResult(walletManager)
|
||||
} catch (e: BlockchainSdkError.AccountNotFound) {
|
||||
resultFactory.getNoAccountResult(walletManager)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to update a wallet manager for: ${walletManager.wallet.blockchain}")
|
||||
|
||||
UpdateWalletManagerResult.Unreachable
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getOrCreateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager? {
|
||||
val userWalletId = userWallet.walletId
|
||||
|
||||
var walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath?.rawPath,
|
||||
)
|
||||
|
||||
if (walletManager == null) {
|
||||
walletManager = walletManagerFactory.createWalletManager(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
) ?: return null
|
||||
|
||||
walletManagersStore.store(userWalletId, walletManager)
|
||||
}
|
||||
|
||||
return walletManager
|
||||
}
|
||||
|
||||
private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
val tokensToAdd = sdkTokenConverter
|
||||
.convertList(tokens.toList())
|
||||
.filter { it !in walletManager.cardTokens }
|
||||
|
||||
walletManager.addTokens(tokensToAdd)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.domain.walletmanager
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
// TODO: Move to its own module
|
||||
/**
|
||||
* A facade for managing wallets.
|
||||
*/
|
||||
interface WalletManagerFacade {
|
||||
|
||||
/**
|
||||
* Updates the wallet manager associated with a user's wallet and network.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param networkId The network ID.
|
||||
* @param extraTokens Additional tokens.
|
||||
* @return The result of updating the wallet manager.
|
||||
*/
|
||||
suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class CryptoCurrencyAmount {
|
||||
|
||||
abstract val value: BigDecimal
|
||||
|
||||
data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount()
|
||||
|
||||
data class Token(
|
||||
val tokenContractAddress: String,
|
||||
override val value: BigDecimal,
|
||||
) : CryptoCurrencyAmount()
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class UpdateWalletManagerResult {
|
||||
|
||||
object MissedDerivation : UpdateWalletManagerResult()
|
||||
|
||||
object Unreachable : UpdateWalletManagerResult()
|
||||
|
||||
data class Verified(
|
||||
val tokensAmounts: Set<CryptoCurrencyAmount>,
|
||||
val hasTransactionsInProgress: Boolean, // TODO: May be add recent transactions
|
||||
) : UpdateWalletManagerResult()
|
||||
|
||||
data class NoAccount(val amountToCreateAccount: BigDecimal) : UpdateWalletManagerResult()
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
internal class SdkTokenConverter : Converter<CryptoCurrency.Token, SdkToken> {
|
||||
|
||||
override fun convert(value: CryptoCurrency.Token): SdkToken {
|
||||
return SdkToken(
|
||||
id = value.id.value.takeUnless { value.isCustom },
|
||||
name = value.name,
|
||||
symbol = value.symbol,
|
||||
contractAddress = value.contractAddress,
|
||||
decimals = value.decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.domain.common.extensions.amountToCreateAccount
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class UpdateWalletManagerResultFactory {
|
||||
|
||||
fun getResult(walletManager: WalletManager): UpdateWalletManagerResult.Verified {
|
||||
val hasNotConfirmedTransactions = walletManager.wallet
|
||||
.recentTransactions
|
||||
.any { it.status != TransactionStatus.Confirmed }
|
||||
|
||||
val amounts = walletManager.wallet.amounts
|
||||
|
||||
return UpdateWalletManagerResult.Verified(
|
||||
tokensAmounts = getTokensAmounts(amounts.values.toSet()),
|
||||
hasTransactionsInProgress = hasNotConfirmedTransactions,
|
||||
)
|
||||
}
|
||||
|
||||
fun getDemoResult(demoAmount: Amount, tokens: Set<CryptoCurrency.Token>): UpdateWalletManagerResult.Verified {
|
||||
return UpdateWalletManagerResult.Verified(
|
||||
tokensAmounts = getDemoTokensAmounts(demoAmount, tokens),
|
||||
hasTransactionsInProgress = false,
|
||||
)
|
||||
}
|
||||
|
||||
fun getNoAccountResult(walletManager: WalletManager): UpdateWalletManagerResult.NoAccount {
|
||||
val wallet = walletManager.wallet
|
||||
val blockchain = wallet.blockchain
|
||||
val amountToCreateAccount = blockchain.amountToCreateAccount(
|
||||
token = wallet.getTokens().firstOrNull(),
|
||||
)
|
||||
|
||||
requireNotNull(amountToCreateAccount) {
|
||||
"Unable to get required amount to create account for: $blockchain"
|
||||
}
|
||||
|
||||
return UpdateWalletManagerResult.NoAccount(amountToCreateAccount)
|
||||
}
|
||||
|
||||
private fun getTokensAmounts(amounts: Set<Amount>): Set<CryptoCurrencyAmount> {
|
||||
val mutableAmounts = hashSetOf<CryptoCurrencyAmount>()
|
||||
|
||||
return amounts.mapNotNullTo(mutableAmounts, ::getTokenAmount)
|
||||
}
|
||||
|
||||
private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set<CryptoCurrency.Token>): Set<CryptoCurrencyAmount> {
|
||||
val amountValue = demoAmount.value ?: BigDecimal.ZERO
|
||||
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
||||
|
||||
return tokens.mapTo(demoAmounts) { token ->
|
||||
CryptoCurrencyAmount.Token(token.contractAddress, amountValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTokenAmount(amount: Amount): CryptoCurrencyAmount? {
|
||||
return when (val type = amount.type) {
|
||||
is AmountType.Token -> CryptoCurrencyAmount.Token(
|
||||
tokenContractAddress = type.token.contractAddress,
|
||||
value = getAmountValue(amount) ?: return null,
|
||||
)
|
||||
is AmountType.Coin -> CryptoCurrencyAmount.Coin(
|
||||
value = getAmountValue(amount) ?: return null,
|
||||
)
|
||||
is AmountType.Reserve -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAmountValue(amount: Amount): BigDecimal? {
|
||||
val value = amount.value
|
||||
|
||||
if (value == null) {
|
||||
Timber.e("Amount not found for currency: ${amount.currencySymbol}")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.extensions.makeWalletManagerForApp
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
internal class WalletManagerFactory(
|
||||
private val configManager: ConfigManager,
|
||||
) {
|
||||
|
||||
private val sdkWalletManagerFactory by lazy {
|
||||
WalletManagerFactory(configManager.config.blockchainSdkConfig)
|
||||
}
|
||||
|
||||
fun createWalletManager(
|
||||
scanResponse: ScanResponse,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager? {
|
||||
val derivationParams = getDerivationParams(derivationPath, scanResponse.card)
|
||||
|
||||
return sdkWalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationParams = derivationParams,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDerivationParams(derivationPath: DerivationPath?, card: CardDTO): DerivationParams? {
|
||||
val derivationStyle = when {
|
||||
!card.settings.isHDWalletAllowed -> return null
|
||||
card.useOldStyleDerivation -> DerivationStyle.LEGACY
|
||||
else -> DerivationStyle.NEW
|
||||
}
|
||||
|
||||
return if (derivationPath == null) {
|
||||
DerivationParams.Default(derivationStyle)
|
||||
} else {
|
||||
DerivationParams.Custom(derivationPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue