Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-21 12:04:51 +04:00
parent 23d73ac0fa
commit 9db902b25c
7 changed files with 20 additions and 372 deletions

View file

@ -10,7 +10,7 @@ import com.tangem.tap.common.TestActions
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
@ -58,7 +58,7 @@ suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result<Wallet> = try
}
}
fun WalletManager.getTopUpUrl(): String? {
internal fun WalletManager.getTopUpUrl(): String? {
val globalState = store.state.globalState
val defaultAddress = wallet.address
@ -72,18 +72,18 @@ fun WalletManager.getTopUpUrl(): String? {
)
}
fun WalletManager?.getAddressData(): WalletDataModel.AddressData? {
internal fun WalletManager?.getAddressData(): WalletAddressData? {
val wallet = this?.wallet ?: return null
val addressDataList = wallet.createAddressesData()
return if (addressDataList.isEmpty()) null else addressDataList[0]
}
fun Wallet.createAddressesData(): List<WalletDataModel.AddressData> {
val listOfAddressData = mutableListOf<WalletDataModel.AddressData>()
private fun Wallet.createAddressesData(): List<WalletAddressData> {
val listOfAddressData = mutableListOf<WalletAddressData>()
// put a defaultAddress at the first place
addresses.forEach {
val addressData = WalletDataModel.AddressData(
val addressData = WalletAddressData(
it.value,
it.type,
getShareUri(it.value),

View file

@ -3,7 +3,7 @@ package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.TestAction
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.wallet.R
@ -20,7 +20,7 @@ sealed class AppDialog : StateDialog {
internal data class AddressInfoDialog(
val currency: Currency,
val addressData: WalletDataModel.AddressData,
val addressData: WalletAddressData,
) : AppDialog()
data class TestActionsDialog(

View file

@ -9,7 +9,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
@ -42,7 +42,7 @@ internal class AddressInfoBottomSheetDialog(
showData(data = stateDialog.addressData)
}
private fun showData(data: WalletDataModel.AddressData) = with(binding!!) {
private fun showData(data: WalletAddressData) = with(binding!!) {
pseudoToolbar.imvClose.setOnClickListener {
dismissWithAnimation = true
cancel()

View file

@ -0,0 +1,10 @@
package com.tangem.tap.domain.model
import com.tangem.blockchain.common.address.AddressType
internal data class WalletAddressData(
val address: String,
val type: AddressType,
val shareUrl: String,
val exploreUrl: String,
)

View file

@ -1,88 +0,0 @@
package com.tangem.tap.domain.model
import com.tangem.blockchain.common.address.AddressType
import com.tangem.tap.domain.model.WalletDataModel.AddressData
import com.tangem.tap.domain.model.WalletDataModel.Status
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction
import java.math.BigDecimal
/**
* Contains info about wallet store currency
* @param currency Wallet's [Currency]
* @param status Wallet's [Status], represents current status of that currency
* @param walletAddresses List of wallet data [AddressData]
* @param existentialDeposit Amount that must be held on currency's balance, if balance is below that amount all
* founds will be destroyed. Null if currency don't have existential deposit
* @param fiatRate Wallet's fiat rate, used to calculate fiat balance. Null if not provided
* @param isCardSingleToken shows that [Currency] is a card token
* @param isCustom shows that currency is a custom
* */
data class WalletDataModel(
val currency: Currency,
val status: Status,
// FIXME: Left only selected wallet address here and move list of wallet addresses to WalletStoreModel
val walletAddresses: WalletAddresses?,
val existentialDeposit: BigDecimal?,
val fiatRate: BigDecimal?,
val isCardSingleToken: Boolean,
val isCustom: Boolean,
) {
data class WalletAddresses(
val selectedAddress: AddressData,
val list: List<AddressData>,
)
data class AddressData(
val address: String,
val type: AddressType,
val shareUrl: String,
val exploreUrl: String,
)
/**
* Represent current status of currency
* @property amount Currency amount
* @property pendingTransactions List of currency [PendingTransaction] sent in currency's blockchain
* @property errorMessage Status error message, null if not provided
* @property isErrorStatus true if current status is error status, false otherwise
* */
sealed class Status {
open val amount: BigDecimal = BigDecimal.ZERO
open val pendingTransactions: List<PendingTransaction> = emptyList()
open val errorMessage: String? = null
open val isErrorStatus: Boolean = false
}
object Loading : Status()
data class VerifiedOnline(
override val amount: BigDecimal,
) : Status()
data class TransactionInProgress(
override val amount: BigDecimal,
override val pendingTransactions: List<PendingTransaction>,
) : Status()
data class SameCurrencyTransactionInProgress(
override val amount: BigDecimal,
override val pendingTransactions: List<PendingTransaction>,
) : Status()
data class NoAccount(
val amountToCreateAccount: BigDecimal?,
) : Status()
data class Unreachable(
override val errorMessage: String?,
override val amount: BigDecimal = BigDecimal.ZERO,
) : Status() {
override val isErrorStatus: Boolean = true
}
object MissedDerivation : Status() {
override val isErrorStatus: Boolean = true
}
}

View file

@ -1,86 +0,0 @@
package com.tangem.tap.domain.model
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.model.WalletStoreModel.WalletRent
import com.tangem.tap.features.wallet.models.Currency
import java.math.BigDecimal
// FIXME: Move list of wallet addresses from WalletDataModel to this class
/**
* Contains info about the blockchain and its currencies
*
* @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 [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
*
* @property blockchainWalletData Returns the [WalletDataModel] of the blockchain of this wallet store
* or throw [NoSuchElementException] if this wallet store not contains [WalletDataModel] of the blockchain
* */
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 blockchainWalletData: WalletDataModel
get() = walletsData.first { it.currency is Currency.Blockchain }
/**
* Represents wallet blockchain rent
* @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than
* the [WalletRent.exemptionAmount]
* @param exemptionAmount Amount that should be on the blockchain balance not to pay rent
* */
data class WalletRent(
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,188 +0,0 @@
package com.tangem.tap.domain.model.builders
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.extensions.createAddressesData
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.Currency
import java.math.BigDecimal
interface WalletStoreBuilder {
fun build(): WalletStoreModel
interface BlockchainNetworkWalletStoreBuilder : WalletStoreBuilder {
fun walletManager(walletManager: WalletManager?): WalletStoreBuilder
}
interface WalletMangerWalletStoreBuilder : WalletStoreBuilder
companion object {
operator fun invoke(
userWallet: UserWallet,
blockchainNetwork: BlockchainNetwork,
): BlockchainNetworkWalletStoreBuilder {
return BlockchainNetworkWalletStoreBuilderImpl(userWallet, blockchainNetwork)
}
operator fun invoke(userWallet: UserWallet, walletManager: WalletManager): WalletMangerWalletStoreBuilder {
return WalletMangerWalletStoreBuilderImpl(userWallet, walletManager)
}
}
}
private class BlockchainNetworkWalletStoreBuilderImpl(
private val userWallet: UserWallet,
private val blockchainNetwork: BlockchainNetwork,
) : WalletStoreBuilder.BlockchainNetworkWalletStoreBuilder {
private var walletManager: WalletManager? = null
override fun walletManager(walletManager: WalletManager?) = this.apply {
this.walletManager = walletManager
}
override fun build(): WalletStoreModel {
val cardDerivationStyle = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle()
val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle)
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(
walletManager = walletManager,
cardDerivationStyle = cardDerivationStyle,
primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(),
)
return WalletStoreModel(
userWalletId = userWallet.walletId,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath?.let { DerivationPath(it) },
walletsData = listOf(blockchainWalletData) + tokensWalletsData,
walletRent = null,
walletManager = walletManager,
blockchainNetwork = blockchainNetwork,
)
}
}
private class WalletMangerWalletStoreBuilderImpl(
private val userWallet: UserWallet,
private val walletManager: WalletManager,
) : WalletStoreBuilder.WalletMangerWalletStoreBuilder {
override fun build(): WalletStoreModel {
val wallet = walletManager.wallet
val blockchainWalletData = wallet.blockchain.toBlockchainWalletData(walletManager)
val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData(
walletManager = walletManager,
primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(),
)
return WalletStoreModel(
userWalletId = userWallet.walletId,
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath,
walletsData = listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData),
walletRent = null,
walletManager = walletManager,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager),
)
}
}
private fun BlockchainNetwork.getBlockchainWalletData(
walletManager: WalletManager?,
cardDerivationStyle: DerivationStyle?,
): WalletDataModel {
val currency = Currency.Blockchain(
blockchain = blockchain,
derivationPath = derivationPath,
)
return WalletDataModel(
currency = currency,
status = WalletDataModel.Loading,
walletAddresses = walletManager?.wallet?.getWalletAddresses(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = false,
isCustom = currency.isCustomCurrency(cardDerivationStyle),
)
}
private fun BlockchainNetwork.getTokensWalletsData(
walletManager: WalletManager?,
cardDerivationStyle: DerivationStyle?,
primaryToken: Token?,
): List<WalletDataModel> {
return this.tokens
.map { token ->
val currency = Currency.Token(
token = token,
blockchain = blockchain,
derivationPath = derivationPath,
)
WalletDataModel(
currency = currency,
status = WalletDataModel.Loading,
walletAddresses = walletManager?.wallet?.getWalletAddresses(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = token == primaryToken,
isCustom = currency.isCustomCurrency(cardDerivationStyle),
)
}
}
private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): WalletDataModel {
val wallet = walletManager.wallet
return WalletDataModel(
currency = Currency.Blockchain(
blockchain = this,
derivationPath = wallet.publicKey.derivationPath?.rawPath,
),
status = WalletDataModel.Loading,
walletAddresses = wallet.getWalletAddresses(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = false,
isCustom = false,
)
}
private fun Token.toTokenWalletData(walletManager: WalletManager, primaryToken: Token?): WalletDataModel {
val wallet = walletManager.wallet
return WalletDataModel(
currency = Currency.Token(
token = this,
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath?.rawPath,
),
status = WalletDataModel.Loading,
walletAddresses = wallet.getWalletAddresses(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = this == primaryToken,
isCustom = false,
)
}
private fun getExistentialDeposit(walletManager: WalletManager?): BigDecimal? {
return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit()
}
private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? {
return this.createAddressesData()
.takeIf { it.isNotEmpty() }
?.let { addresses ->
WalletDataModel.WalletAddresses(
list = addresses,
selectedAddress = addresses.first(),
)
}
}