Updated on 2026-08-14

This commit is contained in:
Tangem 2022-12-26 15:06:51 +04:00
parent d1798a2c49
commit 6dee2eeed7
112 changed files with 2146 additions and 1562 deletions

View file

@ -11,10 +11,6 @@ sealed class TotalFiatBalance {
object Loading : TotalFiatBalance()
data class Refreshing(
override val amount: BigDecimal,
) : TotalFiatBalance()
data class Error(
override val amount: BigDecimal,
) : TotalFiatBalance()

View file

@ -6,22 +6,23 @@ import com.tangem.domain.common.util.UserWalletId
/**
* Represents user's wallet which stored in app persistence
* @param name User's wallet name
* @param walletId User's wallet [UserWalletId]
* @param name User wallet name
* @param walletId User wallet [UserWalletId]
* @param artworkUrl User wallet card artwork URL
* @param cardsInWallet List of cards IDs assigned with this user's wallet
* @param isMultiCurrency Indicates whether this user wallet can work with more than one currency
* @param scanResponse [ScanResponse] of primary user's wallet card.
* TODO: Replace with [com.tangem.domain.common.CardDTO]
* @property cardId ID of user's wallet primary card
* @property hasAccessCode Indicates if the user's wallet primary card has access code
* @property isLocked Indicates if this primary card has no currency wallets
* @property isSaved Indicates if this user wallet is saved
* */
data class UserWallet(
val name: String,
val walletId: UserWalletId,
val artworkUrl: String,
val cardsInWallet: Set<String>,
val isMultiCurrency: Boolean,
val scanResponse: ScanResponse,
) {
val cardId: String
@ -32,17 +33,18 @@ data class UserWallet(
val isLocked: Boolean
get() = scanResponse.card.wallets.isEmpty()
internal var isSaved: Boolean = true
}
/**
* !!! Workaround !!!
*
* Calculate same [UserWalletId] for twins instead
*
* TODO: Remove after [REDACTED_JIRA]
* */
fun UserWallet.isTwinnedWith(other: UserWallet): Boolean {
if (!scanResponse.isTangemTwins()) return false
if (!scanResponse.isTangemTwins() || !other.scanResponse.isTangemTwins()) return false
if (scanResponse.secondTwinPublicKey == null || other.scanResponse.secondTwinPublicKey == null) return false
if (other.scanResponse.secondTwinPublicKey == scanResponse.card.wallets.firstOrNull()?.publicKey?.toHexString()) {
return true
}

View file

@ -35,12 +35,6 @@ data class WalletDataModel(
open val pendingTransactions: List<PendingTransaction> = emptyList()
open val errorMessage: String? = null
open val isErrorStatus: Boolean = false
fun asRefreshing() = Refreshing(
amount = amount,
pendingTransactions = pendingTransactions,
errorMessage = errorMessage,
)
}
object Loading : Status()
@ -74,12 +68,4 @@ data class WalletDataModel(
object MissedDerivation : Status() {
override val isErrorStatus: Boolean = true
}
data class Refreshing(
override val amount: BigDecimal,
override val pendingTransactions: List<PendingTransaction>,
override val errorMessage: String?,
) : Status() {
override val isErrorStatus: Boolean = errorMessage != null
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.domain.model
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.domain.model.WalletStoreModel.WalletRent
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
@ -8,21 +10,28 @@ import java.math.BigDecimal
/**
* Contains info about the blockchain and its currencies
* @param userWalletId ID of the [UserWallet] which uses that store
* @param blockchainNetwork Store's [BlockchainNetwork]
* @param walletManager Store's [WalletManager], may be null if it fails to create this manager. TODO: Remove after
* WalletMiddleware refactoring
* @param userWalletId ID of the associated [UserWallet]
* @param blockchain [Blockchain] of this WalletStore
* @param derivationPath [DerivationPath] of this store, null if the card does not support the
* [HD Wallet](https://coinsutra.com/hd-wallets-deterministic-wallet/)
* @param walletsData List of [WalletDataModel] which represents store's blockchain currency and tokens currencies
* @param walletRent Store's [WalletRent], null if store has no rent or currency balance is greater then
* @param walletRent [WalletRent], null if store has no rent or currency balance is greater then
* [WalletRent.exemptionAmount]
* @param blockchainNetwork [BlockchainNetwork].
* TODO: Remove after WalletMiddleware refactoring
* @param walletManager [WalletManager], may be null if it fails to create this manager.
* TODO: Remove after WalletMiddleware refactoring
* */
data class WalletStoreModel(
val userWalletId: UserWalletId,
val blockchain: Blockchain,
val derivationPath: DerivationPath?,
val walletsData: List<WalletDataModel>,
val walletRent: WalletRent?,
@Deprecated("Don't use it, will be removed")
val blockchainNetwork: BlockchainNetwork,
@Deprecated("Don't use it, will be removed")
val walletManager: WalletManager?,
val walletsData: List<WalletDataModel>,
val walletRent: WalletRent?,
) {
/**
@ -35,4 +44,34 @@ data class WalletStoreModel(
val rent: BigDecimal,
val exemptionAmount: BigDecimal,
)
// TODO: Remove the generated methods after blockchainNetwork and walletManager are removed from the model
// region Generated
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is WalletStoreModel) return false
if (userWalletId != other.userWalletId) return false
if (blockchain != other.blockchain) return false
if (derivationPath != other.derivationPath) return false
if (walletsData != other.walletsData) return false
if (walletRent != other.walletRent) return false
return true
}
override fun hashCode(): Int {
var result = userWalletId.hashCode()
result = 31 * result + blockchain.hashCode()
result = 31 * result + (derivationPath?.hashCode() ?: 0)
result = 31 * result + walletsData.hashCode()
result = 31 * result + (walletRent?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return "WalletStoreModel(userWalletId=$userWalletId, blockchain=$blockchain, derivationPath=$derivationPath, " +
"walletsData=$walletsData, walletRent=$walletRent)"
}
// endregion Generated
}

View file

@ -1,15 +1,48 @@
package com.tangem.tap.domain.model.builders
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.TwinsHelper
import com.tangem.domain.common.util.userWalletId
import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.TangemApi
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.features.wallet.redux.Artwork
class UserWalletBuilder(
private val scanResponse: ScanResponse,
private val onlineCardVerifier: OnlineCardVerifier = OnlineCardVerifier(),
) {
private var backupCardsIds: Set<String> = emptySet()
private val CardDTO.isBackupNotAllowed: Boolean
get() = !this.settings.isBackupAllowed
private val ScanResponse.userWalletName: String
get() = when (productType) {
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"
ProductType.SaltPay -> "SaltPay"
ProductType.Wallet -> when {
card.isBackupNotAllowed -> "Tangem card"
card.isStart2Coin -> "Start2Coin"
else -> "Wallet"
}
}
private val ScanResponse.isMultiCurrency: Boolean
get() = when (productType) {
ProductType.Note -> false
ProductType.Twins -> false
ProductType.SaltPay -> false
ProductType.Wallet -> !card.isStart2Coin
}
fun backupCardsIds(backupCardsIds: Set<String>?) = this.apply {
if (backupCardsIds != null) {
this.backupCardsIds = backupCardsIds
@ -20,11 +53,44 @@ class UserWalletBuilder(
return with(scanResponse) {
UserWallet(
walletId = card.userWalletId,
name = productType.name,
artworkUrl = card.getOrLoadCardArtworkUrl(),
name = userWalletName,
artworkUrl = loadArtworkUrl(card.cardId, card.cardPublicKey),
cardsInWallet = backupCardsIds.plus(card.cardId),
scanResponse = this,
isMultiCurrency = isMultiCurrency,
)
}
}
private suspend fun loadArtworkUrl(cardId: String, cardPublicKey: ByteArray): String {
return when (val result = onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = result.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
getFallbackArtworkUrl(cardId)
} else {
getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> getFallbackArtworkUrl(cardId)
}
}
private fun getFallbackArtworkUrl(cardId: String): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> when (TwinsHelper.getTwinCardNumber(cardId)) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
else -> Artwork.DEFAULT_IMG_URL
}
}
}
private fun getUrlForArtwork(cardId: String, cardPublicKeyHex: String, artworkId: String): String {
return TangemApi.Companion.BaseUrl.VERIFY.url + TangemApi.ARTWORK +
"?artworkId=${artworkId}&CID=${cardId}&publicKey=$cardPublicKeyHex"
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
@ -54,10 +55,12 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
return WalletStoreModel(
userWalletId = userWalletId,
blockchainNetwork = blockchainNetwork,
walletManager = walletManager,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath?.let { DerivationPath(it) },
walletsData = (listOf(blockchainWalletData) + tokensWalletsData),
walletRent = null,
walletManager = walletManager,
blockchainNetwork = blockchainNetwork,
)
}
}
@ -74,10 +77,12 @@ private class WalletMangerWalletStoreBuilderImpl(
return WalletStoreModel(
userWalletId = userWalletId,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager),
walletManager = walletManager,
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath,
walletsData = (listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData)),
walletRent = null,
walletManager = walletManager,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager),
)
}
}