Updated on 2026-08-14
This commit is contained in:
commit
7c629b68d1
1179 changed files with 24718 additions and 16703 deletions
|
|
@ -2,7 +2,15 @@ package com.domain.blockaid.models.transaction.simultation
|
|||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class AmountInfo(
|
||||
val amount: BigDecimal,
|
||||
val token: TokenInfo,
|
||||
)
|
||||
sealed class AmountInfo {
|
||||
|
||||
data class FungibleTokens(
|
||||
val amount: BigDecimal,
|
||||
val token: TokenInfo,
|
||||
) : AmountInfo()
|
||||
|
||||
data class NonFungibleTokens(
|
||||
val name: String,
|
||||
val logoUrl: String?,
|
||||
) : AmountInfo()
|
||||
}
|
||||
|
|
@ -4,4 +4,5 @@ data class TokenInfo(
|
|||
val chainId: Int?,
|
||||
val logoUrl: String?,
|
||||
val symbol: String,
|
||||
val decimals: Int,
|
||||
)
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.card
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class DerivePublicKeysUseCase(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.card
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.calculateRipemd160
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
|
|
@ -9,7 +10,7 @@ import com.tangem.crypto.NetworkType
|
|||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
|
@ -26,7 +27,7 @@ class GetExtendedPublicKeyForCurrencyUseCase(
|
|||
val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found")
|
||||
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
|
||||
val hdKey = if (isSecp256k1Blockchain) {
|
||||
|
|
@ -76,7 +77,7 @@ class GetExtendedPublicKeyForCurrencyUseCase(
|
|||
val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found")
|
||||
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
val isHdKey = userWallet.wallet.publicKey.derivationType?.hdKey
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.card
|
||||
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.tangem.domain.card
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.domain.common.util.hasDerivation
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
class NetworkHasDerivationUseCase {
|
||||
|
||||
operator fun invoke(scanResponse: ScanResponse, network: Network): Either<Throwable, Boolean> {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value
|
||||
return Either.catch { derivationPath != null && scanResponse.hasDerivation(blockchain, derivationPath) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.domain.card.repository
|
|||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.card.BackendId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ interface DerivationsRepository {
|
|||
@Throws
|
||||
suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||
|
||||
suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.ID>)
|
||||
suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.RawID>)
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.domain.feedback.repository
|
||||
|
||||
interface FeedbackFeatureToggles {
|
||||
val isUsedeskEnabled: Boolean
|
||||
}
|
||||
|
|
@ -9,6 +9,10 @@ android {
|
|||
namespace = "com.tangem.domain.features"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
|
@ -43,8 +47,11 @@ dependencies {
|
|||
ksp(deps.moshi.kotlin.codegen)
|
||||
|
||||
/** Testing libraries */
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(projects.common.test)
|
||||
androidTestImplementation(deps.test.junit.android)
|
||||
androidTestImplementation(deps.test.espresso)
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ val ScanResponse.derivationStyleProvider: DerivationStyleProvider
|
|||
val CardDTO.derivationStyleProvider: DerivationStyleProvider
|
||||
get() = TangemDerivationStyleProvider(this)
|
||||
|
||||
val UserWallet.cardTypesResolver: CardTypesResolver
|
||||
val UserWallet.Cold.cardTypesResolver: CardTypesResolver
|
||||
get() = scanResponse.cardTypesResolver
|
||||
|
||||
fun ScanResponse.twinsIsTwinned(): Boolean = card.isTangemTwins && walletData != null && secondTwinPublicKey != null
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun UserWallet.getCardsCount(): Int? = scanResponse.getCardsCount()
|
||||
fun UserWallet.Cold.getCardsCount(): Int? = scanResponse.getCardsCount()
|
||||
|
||||
/**
|
||||
* Get backup cards count for a card that was saved in [UserWallet]
|
||||
*
|
||||
* @return null if wallet is not multi-currency or total cards count
|
||||
*/
|
||||
fun UserWallet.getBackupCardsCount(): Int? = scanResponse.getBackupCardsCount()
|
||||
fun UserWallet.Cold.getBackupCardsCount(): Int? = scanResponse.getBackupCardsCount()
|
||||
|
|
@ -2,8 +2,8 @@ package com.tangem.domain.exchange
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.utils
|
|||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.blockchain.common.Amount as SdkAmount
|
||||
|
||||
|
|
|
|||
|
|
@ -20,26 +20,28 @@ import com.tangem.blockchain.nft.models.NFTAsset
|
|||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
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.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.RentData
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.domain.walletmanager.model.TokenInfo
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.walletmanager.utils.*
|
||||
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.models.requireColdWallet
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
|
@ -78,7 +80,7 @@ class DefaultWalletManagersFacade(
|
|||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value
|
||||
|
||||
return getAndUpdateWalletManager(userWallet, blockchain, derivationPath, extraTokens)
|
||||
|
|
@ -88,7 +90,7 @@ class DefaultWalletManagersFacade(
|
|||
if (networks.isEmpty()) return
|
||||
|
||||
val blockchainsToDerivationPaths = networks.map {
|
||||
Blockchain.fromId(it.id.value) to it.derivationPath.value
|
||||
it.toBlockchain() to it.derivationPath.value
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
|
|
@ -141,7 +143,7 @@ class DefaultWalletManagersFacade(
|
|||
withContext(dispatchers.io) {
|
||||
val walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = Blockchain.fromId(network.id.value),
|
||||
blockchain = network.toBlockchain(),
|
||||
derivationPath = network.derivationPath.value,
|
||||
) ?: return@withContext
|
||||
|
||||
|
|
@ -158,10 +160,13 @@ class DefaultWalletManagersFacade(
|
|||
network: Network,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val derivationPath = network.derivationPath.value
|
||||
|
||||
if (derivationPath != null && !userWallet.scanResponse.hasDerivation(blockchain, derivationPath)) {
|
||||
if (derivationPath != null &&
|
||||
userWallet is UserWallet.Cold &&
|
||||
!userWallet.scanResponse.hasDerivation(blockchain, derivationPath)
|
||||
) {
|
||||
Timber.w("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
|
@ -181,7 +186,7 @@ class DefaultWalletManagersFacade(
|
|||
addressType: AddressType,
|
||||
contractAddress: String?,
|
||||
): String {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
|
|
@ -234,7 +239,7 @@ class DefaultWalletManagersFacade(
|
|||
currency: CryptoCurrency,
|
||||
page: Page,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem> {
|
||||
): PaginationWrapper<TxInfo> {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
network = currency.network,
|
||||
|
|
@ -285,9 +290,10 @@ class DefaultWalletManagersFacade(
|
|||
derivationPath: String?,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val scanResponse = userWallet.scanResponse
|
||||
|
||||
if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath)) {
|
||||
if (derivationPath != null &&
|
||||
userWallet is UserWallet.Cold &&
|
||||
!userWallet.scanResponse.hasDerivation(blockchain, derivationPath)
|
||||
) {
|
||||
Timber.w("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
|
@ -305,7 +311,7 @@ class DefaultWalletManagersFacade(
|
|||
updateWalletManagerTokensIfNeeded(walletManager, extraTokens)
|
||||
|
||||
return try {
|
||||
if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
if (userWallet is UserWallet.Cold && demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
|
||||
updateDemoWalletManager(walletManager)
|
||||
} else {
|
||||
updateWalletManager(walletManager)
|
||||
|
|
@ -364,6 +370,9 @@ class DefaultWalletManagersFacade(
|
|||
): WalletManager? {
|
||||
initMutex.withLock {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
|
||||
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
|
||||
|
||||
var walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -385,7 +394,7 @@ class DefaultWalletManagersFacade(
|
|||
|
||||
@Deprecated("Will be removed in future")
|
||||
override suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager? {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
return getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -477,7 +486,7 @@ class DefaultWalletManagersFacade(
|
|||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Result<TransactionFee>? = withContext(dispatchers.io) {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -534,10 +543,7 @@ class DefaultWalletManagersFacade(
|
|||
return walletManager?.createTransaction(amount, fee, destination)
|
||||
}
|
||||
|
||||
override suspend fun getRecentTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): List<TxHistoryItem> {
|
||||
override suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxInfo> {
|
||||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
|
||||
|
||||
if (walletManager == null) {
|
||||
|
|
@ -569,7 +575,7 @@ class DefaultWalletManagersFacade(
|
|||
decimals: Int,
|
||||
id: String?,
|
||||
): BigDecimal {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -640,7 +646,7 @@ class DefaultWalletManagersFacade(
|
|||
}
|
||||
|
||||
override suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -651,7 +657,7 @@ class DefaultWalletManagersFacade(
|
|||
}
|
||||
|
||||
override suspend fun getNFTCollections(userWalletId: UserWalletId, network: Network): List<NFTCollection> {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -666,7 +672,7 @@ class DefaultWalletManagersFacade(
|
|||
network: Network,
|
||||
collectionIdentifier: NFTCollection.Identifier,
|
||||
): List<NFTAsset> {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -682,7 +688,7 @@ class DefaultWalletManagersFacade(
|
|||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset? {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -697,7 +703,7 @@ class DefaultWalletManagersFacade(
|
|||
collectionIdentifier: NFTCollection.Identifier,
|
||||
assetIdentifier: NFTAsset.Identifier,
|
||||
): NFTAsset.SalePrice? {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
|
|
@ -707,7 +713,7 @@ class DefaultWalletManagersFacade(
|
|||
}
|
||||
|
||||
override suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String? {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val blockchain = network.toBlockchain()
|
||||
return blockchain.getNFTExploreUrl(assetIdentifier)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ import com.tangem.blockchain.extensions.Result
|
|||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.RentData
|
||||
import com.tangem.domain.walletmanager.model.TokenInfo
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -106,7 +106,7 @@ interface WalletManagersFacade {
|
|||
currency: CryptoCurrency,
|
||||
page: Page,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem>
|
||||
): PaginationWrapper<TxInfo>
|
||||
|
||||
@Deprecated("Will be removed in future")
|
||||
suspend fun getOrCreateWalletManager(
|
||||
|
|
@ -216,7 +216,7 @@ interface WalletManagersFacade {
|
|||
): TransactionData?
|
||||
|
||||
/** Get recent transactions of [userWalletId] for [currency] */
|
||||
suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxHistoryItem>
|
||||
suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxInfo>
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun tokenBalance(
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
data class Address(
|
||||
val value: String,
|
||||
val type: Type,
|
||||
) {
|
||||
|
||||
enum class Type {
|
||||
Primary, Secondary,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class CryptoCurrencyAmount {
|
||||
|
||||
abstract val value: BigDecimal
|
||||
|
||||
data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount()
|
||||
|
||||
data class Token(
|
||||
val tokenId: CryptoCurrency.RawID?,
|
||||
val tokenContractAddress: String,
|
||||
override val value: BigDecimal,
|
||||
) : CryptoCurrencyAmount()
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
|
||||
// TODO: [REDACTED_JIRA] move to txhistory module
|
||||
sealed class CryptoCurrencyTransaction {
|
||||
|
||||
abstract val txHistoryItem: TxHistoryItem
|
||||
|
||||
data class Coin(override val txHistoryItem: TxHistoryItem) : CryptoCurrencyTransaction()
|
||||
|
||||
data class Token(
|
||||
val tokenId: String?,
|
||||
val tokenContractAddress: String,
|
||||
override val txHistoryItem: TxHistoryItem,
|
||||
) : CryptoCurrencyTransaction()
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
data class TokenInfo(
|
||||
val network: Network,
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.domain.walletmanager.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class UpdateWalletManagerResult {
|
||||
|
||||
data object MissedDerivation : UpdateWalletManagerResult()
|
||||
|
||||
data class Unreachable(
|
||||
val selectedAddress: String? = null,
|
||||
val addresses: Set<Address>? = null,
|
||||
) : UpdateWalletManagerResult()
|
||||
|
||||
data class Verified(
|
||||
val selectedAddress: String,
|
||||
val addresses: Set<Address>,
|
||||
val currenciesAmounts: Set<CryptoCurrencyAmount>,
|
||||
val currentTransactions: Set<CryptoCurrencyTransaction>,
|
||||
) : UpdateWalletManagerResult()
|
||||
|
||||
data class NoAccount(
|
||||
val selectedAddress: String,
|
||||
val addresses: Set<Address>,
|
||||
val amountToCreateAccount: BigDecimal,
|
||||
val errorMessage: String,
|
||||
) : UpdateWalletManagerResult()
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.walletmanager.utils
|
|||
|
||||
import com.tangem.blockchain.common.CryptoCurrencyType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class CryptoCurrencyTypeConverter : Converter<CryptoCurrency, CryptoCurrencyType> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.address.Address as SdkAddress
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem as SdkTransactionHistoryItem
|
||||
|
||||
internal class SdkTransactionHistoryItemConverter(
|
||||
smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<SdkTransactionHistoryItem, TxHistoryItem> {
|
||||
) : Converter<SdkTransactionHistoryItem, TxInfo> {
|
||||
|
||||
private val typeConverter by lazy { SdkTransactionTypeConverter(smartContractMethods) }
|
||||
|
||||
override fun convert(value: SdkTransactionHistoryItem): TxHistoryItem = TxHistoryItem(
|
||||
override fun convert(value: SdkTransactionHistoryItem): TxInfo = TxInfo(
|
||||
txHash = value.txHash,
|
||||
timestampInMillis = value.timestamp,
|
||||
isOutgoing = value.isOutgoing,
|
||||
|
|
@ -20,35 +20,35 @@ internal class SdkTransactionHistoryItemConverter(
|
|||
sourceType = value.sourceType.toDomain(),
|
||||
interactionAddressType = value.extractInteractionAddressType(),
|
||||
status = when (value.status) {
|
||||
SdkTransactionHistoryItem.TransactionStatus.Confirmed -> TxHistoryItem.TransactionStatus.Confirmed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Failed -> TxHistoryItem.TransactionStatus.Failed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Unconfirmed -> TxHistoryItem.TransactionStatus.Unconfirmed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Confirmed -> TxInfo.TransactionStatus.Confirmed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Failed -> TxInfo.TransactionStatus.Failed
|
||||
SdkTransactionHistoryItem.TransactionStatus.Unconfirmed -> TxInfo.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = typeConverter.convert(value.type),
|
||||
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
|
||||
)
|
||||
|
||||
private fun SdkTransactionHistoryItem.SourceType.toDomain(): TxHistoryItem.SourceType = when (this) {
|
||||
is TransactionHistoryItem.SourceType.Single -> TxHistoryItem.SourceType.Single(address)
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxHistoryItem.SourceType.Multiple(addresses)
|
||||
private fun SdkTransactionHistoryItem.SourceType.toDomain(): TxInfo.SourceType = when (this) {
|
||||
is TransactionHistoryItem.SourceType.Single -> TxInfo.SourceType.Single(address)
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxInfo.SourceType.Multiple(addresses)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.DestinationType.toDomain(): TxHistoryItem.DestinationType = when (this) {
|
||||
is SdkTransactionHistoryItem.DestinationType.Single -> TxHistoryItem.DestinationType.Single(
|
||||
private fun SdkTransactionHistoryItem.DestinationType.toDomain(): TxInfo.DestinationType = when (this) {
|
||||
is SdkTransactionHistoryItem.DestinationType.Single -> TxInfo.DestinationType.Single(
|
||||
addressType.toDomain(),
|
||||
)
|
||||
is SdkTransactionHistoryItem.DestinationType.Multiple -> TxHistoryItem.DestinationType.Multiple(
|
||||
is SdkTransactionHistoryItem.DestinationType.Multiple -> TxInfo.DestinationType.Multiple(
|
||||
addressTypes.map { it.toDomain() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.AddressType.toDomain(): TxHistoryItem.AddressType = when (this) {
|
||||
is SdkTransactionHistoryItem.AddressType.Contract -> TxHistoryItem.AddressType.Contract(address)
|
||||
is SdkTransactionHistoryItem.AddressType.User -> TxHistoryItem.AddressType.User(address)
|
||||
is SdkTransactionHistoryItem.AddressType.Validator -> TxHistoryItem.AddressType.Validator(address)
|
||||
private fun SdkTransactionHistoryItem.AddressType.toDomain(): TxInfo.AddressType = when (this) {
|
||||
is SdkTransactionHistoryItem.AddressType.Contract -> TxInfo.AddressType.Contract(address)
|
||||
is SdkTransactionHistoryItem.AddressType.User -> TxInfo.AddressType.User(address)
|
||||
is SdkTransactionHistoryItem.AddressType.Validator -> TxInfo.AddressType.Validator(address)
|
||||
}
|
||||
|
||||
private fun SdkTransactionHistoryItem.extractInteractionAddressType(): TxHistoryItem.InteractionAddressType? {
|
||||
private fun SdkTransactionHistoryItem.extractInteractionAddressType(): TxInfo.InteractionAddressType? {
|
||||
return when (val transactionType = type) {
|
||||
SdkTransactionHistoryItem.TransactionType.Transfer -> if (isOutgoing) {
|
||||
mapToInteractionAddressType(destinationType = destinationType)
|
||||
|
|
@ -61,7 +61,7 @@ internal class SdkTransactionHistoryItemConverter(
|
|||
-> mapToInteractionAddressType(destinationType = destinationType)
|
||||
|
||||
is SdkTransactionHistoryItem.TransactionType.TronStakingTransactionType.VoteWitnessContract -> {
|
||||
TxHistoryItem.InteractionAddressType.Validator(address = transactionType.validatorAddress)
|
||||
TxInfo.InteractionAddressType.Validator(address = transactionType.validatorAddress)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -69,19 +69,19 @@ internal class SdkTransactionHistoryItemConverter(
|
|||
|
||||
private fun mapToInteractionAddressType(
|
||||
destinationType: SdkTransactionHistoryItem.DestinationType,
|
||||
): TxHistoryItem.InteractionAddressType {
|
||||
): TxInfo.InteractionAddressType {
|
||||
return when (destinationType) {
|
||||
is TransactionHistoryItem.DestinationType.Multiple -> TxHistoryItem.InteractionAddressType.Multiple(
|
||||
is TransactionHistoryItem.DestinationType.Multiple -> TxInfo.InteractionAddressType.Multiple(
|
||||
destinationType.addressTypes.map { it.address },
|
||||
)
|
||||
is TransactionHistoryItem.DestinationType.Single -> when (destinationType.addressType) {
|
||||
is TransactionHistoryItem.AddressType.Contract -> TxHistoryItem.InteractionAddressType.Contract(
|
||||
is TransactionHistoryItem.AddressType.Contract -> TxInfo.InteractionAddressType.Contract(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
is TransactionHistoryItem.AddressType.User -> TxHistoryItem.InteractionAddressType.User(
|
||||
is TransactionHistoryItem.AddressType.User -> TxInfo.InteractionAddressType.User(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
is TransactionHistoryItem.AddressType.Validator -> TxHistoryItem.InteractionAddressType.Validator(
|
||||
is TransactionHistoryItem.AddressType.Validator -> TxInfo.InteractionAddressType.Validator(
|
||||
destinationType.addressType.address,
|
||||
)
|
||||
}
|
||||
|
|
@ -90,13 +90,13 @@ internal class SdkTransactionHistoryItemConverter(
|
|||
|
||||
private fun mapToInteractionAddressType(
|
||||
sourceType: SdkTransactionHistoryItem.SourceType,
|
||||
): TxHistoryItem.InteractionAddressType {
|
||||
): TxInfo.InteractionAddressType {
|
||||
return when (sourceType) {
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxHistoryItem.InteractionAddressType.Multiple(
|
||||
is TransactionHistoryItem.SourceType.Multiple -> TxInfo.InteractionAddressType.Multiple(
|
||||
sourceType.addresses,
|
||||
)
|
||||
is TransactionHistoryItem.SourceType.Single -> {
|
||||
TxHistoryItem.InteractionAddressType.User(sourceType.address)
|
||||
TxInfo.InteractionAddressType.User(sourceType.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem.TransactionType
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SdkTransactionTypeConverter(
|
||||
private val smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<TransactionType, TxHistoryItem.TransactionType> {
|
||||
) : Converter<TransactionType, TxInfo.TransactionType> {
|
||||
|
||||
override fun convert(value: TransactionType): TxHistoryItem.TransactionType {
|
||||
override fun convert(value: TransactionType): TxInfo.TransactionType {
|
||||
return when (value) {
|
||||
is TransactionType.ContractMethod -> {
|
||||
getTransactionType(methodName = smartContractMethods[value.id]?.name)
|
||||
|
|
@ -18,49 +18,49 @@ internal class SdkTransactionTypeConverter(
|
|||
getTransactionType(methodName = value.name)
|
||||
}
|
||||
is TransactionType.Transfer -> {
|
||||
TxHistoryItem.TransactionType.Transfer
|
||||
TxInfo.TransactionType.Transfer
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.FreezeBalanceV2Contract -> {
|
||||
TxHistoryItem.TransactionType.Staking.Stake
|
||||
TxInfo.TransactionType.Staking.Stake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.UnfreezeBalanceV2Contract -> {
|
||||
TxHistoryItem.TransactionType.Staking.Unstake
|
||||
TxInfo.TransactionType.Staking.Unstake
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.VoteWitnessContract -> {
|
||||
TxHistoryItem.TransactionType.Staking.Vote(value.validatorAddress)
|
||||
TxInfo.TransactionType.Staking.Vote(value.validatorAddress)
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawBalanceContract -> {
|
||||
TxHistoryItem.TransactionType.Staking.ClaimRewards
|
||||
TxInfo.TransactionType.Staking.ClaimRewards
|
||||
}
|
||||
is TransactionType.TronStakingTransactionType.WithdrawExpireUnfreezeContract -> {
|
||||
TxHistoryItem.TransactionType.Staking.Withdraw
|
||||
TxInfo.TransactionType.Staking.Withdraw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTransactionType(methodName: String?): TxHistoryItem.TransactionType {
|
||||
private fun getTransactionType(methodName: String?): TxInfo.TransactionType {
|
||||
return when (methodName) {
|
||||
"transfer" -> TxHistoryItem.TransactionType.Transfer
|
||||
"approve" -> TxHistoryItem.TransactionType.Approve
|
||||
"swap" -> TxHistoryItem.TransactionType.Swap
|
||||
"transfer" -> TxInfo.TransactionType.Transfer
|
||||
"approve" -> TxInfo.TransactionType.Approve
|
||||
"swap" -> TxInfo.TransactionType.Swap
|
||||
"buyVoucher",
|
||||
"buyVoucherPOL",
|
||||
"delegate",
|
||||
-> TxHistoryItem.TransactionType.Staking.Stake
|
||||
-> TxInfo.TransactionType.Staking.Stake
|
||||
"sellVoucher_new",
|
||||
"sellVoucher_newPOL",
|
||||
"undelegate",
|
||||
-> TxHistoryItem.TransactionType.Staking.Unstake
|
||||
-> TxInfo.TransactionType.Staking.Unstake
|
||||
"unstakeClaimTokens_new",
|
||||
"unstakeClaimTokens_newPOL",
|
||||
"claim",
|
||||
-> TxHistoryItem.TransactionType.Staking.Withdraw
|
||||
-> TxInfo.TransactionType.Staking.Withdraw
|
||||
"withdrawRewards",
|
||||
"withdrawRewardsPOL",
|
||||
-> TxHistoryItem.TransactionType.Staking.ClaimRewards
|
||||
"redelegate" -> TxHistoryItem.TransactionType.Staking.Restake
|
||||
null -> TxHistoryItem.TransactionType.UnknownOperation
|
||||
else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() })
|
||||
-> TxInfo.TransactionType.Staking.ClaimRewards
|
||||
"redelegate" -> TxInfo.TransactionType.Staking.Restake
|
||||
null -> TxInfo.TransactionType.UnknownOperation
|
||||
else -> TxInfo.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Convert [TransactionData] to [TxHistoryItem]
|
||||
* Convert [TransactionData] to [TxInfo]
|
||||
*
|
||||
* @property walletAddresses wallet addresses
|
||||
*
|
||||
|
|
@ -17,30 +17,30 @@ import java.math.BigDecimal
|
|||
internal class TransactionDataToTxHistoryItemConverter(
|
||||
private val walletAddresses: Set<Address>,
|
||||
private val feePaidCurrency: FeePaidCurrency,
|
||||
) : Converter<TransactionData.Uncompiled, TxHistoryItem?> {
|
||||
) : Converter<TransactionData.Uncompiled, TxInfo?> {
|
||||
|
||||
override fun convert(value: TransactionData.Uncompiled): TxHistoryItem? {
|
||||
override fun convert(value: TransactionData.Uncompiled): TxInfo? {
|
||||
val hash = value.hash ?: return null
|
||||
val millis = value.date?.timeInMillis ?: return null
|
||||
val amount = getTransactionAmountValue(value.amount, value.fee?.amount) ?: return null
|
||||
val isOutgoing = value.sourceAddress in walletAddresses.map(Address::value)
|
||||
|
||||
return TxHistoryItem(
|
||||
return TxInfo(
|
||||
txHash = hash,
|
||||
timestampInMillis = millis,
|
||||
isOutgoing = isOutgoing,
|
||||
destinationType = TxHistoryItem.DestinationType.Single(
|
||||
addressType = TxHistoryItem.AddressType.User(value.destinationAddress),
|
||||
destinationType = TxInfo.DestinationType.Single(
|
||||
addressType = TxInfo.AddressType.User(value.destinationAddress),
|
||||
),
|
||||
sourceType = TxHistoryItem.SourceType.Single(value.sourceAddress),
|
||||
interactionAddressType = TxHistoryItem.InteractionAddressType.User(
|
||||
sourceType = TxInfo.SourceType.Single(value.sourceAddress),
|
||||
interactionAddressType = TxInfo.InteractionAddressType.User(
|
||||
address = if (isOutgoing) value.destinationAddress else value.sourceAddress,
|
||||
),
|
||||
status = when (value.status) {
|
||||
TransactionStatus.Confirmed -> TxHistoryItem.TransactionStatus.Confirmed
|
||||
TransactionStatus.Unconfirmed -> TxHistoryItem.TransactionStatus.Unconfirmed
|
||||
TransactionStatus.Confirmed -> TxInfo.TransactionStatus.Confirmed
|
||||
TransactionStatus.Unconfirmed -> TxInfo.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = TxHistoryItem.TransactionType.Transfer,
|
||||
type = TxInfo.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.*
|
||||
import com.tangem.blockchainsdk.utils.amountToCreateAccount
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
|
||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.blockchain.common.address.Address as SdkAddress
|
||||
|
||||
/** Factory for creating [UpdateWalletManagerResult] */
|
||||
internal class UpdateWalletManagerResultFactory {
|
||||
|
||||
fun getResult(walletManager: WalletManager): UpdateWalletManagerResult.Verified {
|
||||
/** Get [Verified] result for [walletManager] */
|
||||
fun getResult(walletManager: WalletManager): Verified {
|
||||
val wallet = walletManager.wallet
|
||||
val addresses = getAvailableAddresses(wallet.addresses)
|
||||
val feePaidCurrency = wallet.blockchain.feePaidCurrency()
|
||||
val txHistoryItemConverter = TransactionDataToTxHistoryItemConverter(addresses, feePaidCurrency)
|
||||
|
||||
return UpdateWalletManagerResult.Verified(
|
||||
return Verified(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = addresses,
|
||||
currenciesAmounts = getTokensAmounts(wallet.amounts.values.toSet()),
|
||||
|
|
@ -27,13 +27,19 @@ internal class UpdateWalletManagerResultFactory {
|
|||
)
|
||||
}
|
||||
|
||||
fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): UpdateWalletManagerResult.Verified {
|
||||
/**
|
||||
* Get demo [Verified] result
|
||||
*
|
||||
* @param walletManager wallet manager
|
||||
* @param demoAmount amount that will be used for demo result
|
||||
*/
|
||||
fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): Verified {
|
||||
val wallet = walletManager.wallet
|
||||
val addresses = getAvailableAddresses(wallet.addresses)
|
||||
val feePaidCurrency = wallet.blockchain.feePaidCurrency()
|
||||
val txHistoryItemConverter = TransactionDataToTxHistoryItemConverter(addresses, feePaidCurrency)
|
||||
|
||||
return UpdateWalletManagerResult.Verified(
|
||||
return Verified(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = addresses,
|
||||
currenciesAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens),
|
||||
|
|
@ -41,6 +47,14 @@ internal class UpdateWalletManagerResultFactory {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get [NoAccount] result.
|
||||
* If unable to get required amount for creating account, [Unreachable] result will be returned.
|
||||
*
|
||||
* @param walletManager wallet manager
|
||||
* @param customMessage custom error message
|
||||
* @param amountToCreateAccount amount to create account
|
||||
*/
|
||||
fun getNoAccountResult(
|
||||
walletManager: WalletManager,
|
||||
customMessage: String,
|
||||
|
|
@ -53,12 +67,12 @@ internal class UpdateWalletManagerResultFactory {
|
|||
|
||||
return if (amount == null) {
|
||||
Timber.w("Unable to get required amount to create account for: $blockchain")
|
||||
UpdateWalletManagerResult.Unreachable(
|
||||
Unreachable(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
)
|
||||
} else {
|
||||
UpdateWalletManagerResult.NoAccount(
|
||||
NoAccount(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
amountToCreateAccount = amount,
|
||||
|
|
@ -67,91 +81,46 @@ internal class UpdateWalletManagerResultFactory {
|
|||
}
|
||||
}
|
||||
|
||||
fun getUnreachableResult(walletManager: WalletManager): UpdateWalletManagerResult {
|
||||
/** Get [Unreachable] result for [walletManager] */
|
||||
fun getUnreachableResult(walletManager: WalletManager): Unreachable {
|
||||
val wallet = walletManager.wallet
|
||||
|
||||
return UpdateWalletManagerResult.Unreachable(
|
||||
return Unreachable(
|
||||
selectedAddress = wallet.address,
|
||||
addresses = getAvailableAddresses(wallet.addresses),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAvailableAddresses(addresses: Set<SdkAddress>): Set<Address> {
|
||||
return SdkAddressToAddressConverter.convertList(addresses).toSet()
|
||||
}
|
||||
|
||||
private fun getTokensAmounts(amounts: Set<Amount>): Set<CryptoCurrencyAmount> {
|
||||
return amounts.mapNotNullTo(hashSetOf(), ::createCurrencyAmount)
|
||||
}
|
||||
|
||||
private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set<Token>): Set<CryptoCurrencyAmount> {
|
||||
val amountValue = demoAmount.value ?: BigDecimal.ZERO
|
||||
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
||||
|
||||
return tokens.mapTo(demoAmounts) { token ->
|
||||
CryptoCurrencyAmount.Token(
|
||||
tokenId = token.id?.let { CryptoCurrency.RawID(it) },
|
||||
tokenContractAddress = token.contractAddress,
|
||||
value = amountValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentTransactions(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
recentTransactions: Set<TransactionData.Uncompiled>,
|
||||
): Set<CryptoCurrencyTransaction> {
|
||||
val unconfirmedTransactions = recentTransactions.filter {
|
||||
it.status == TransactionStatus.Unconfirmed
|
||||
}
|
||||
|
||||
return unconfirmedTransactions.mapNotNullTo(hashSetOf()) {
|
||||
createCurrencyTransaction(
|
||||
txHistoryItemConverter = txHistoryItemConverter,
|
||||
data = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCurrencyAmount(amount: Amount): CryptoCurrencyAmount? {
|
||||
return when (val type = amount.type) {
|
||||
is AmountType.Token -> CryptoCurrencyAmount.Token(
|
||||
tokenId = type.token.id?.let { CryptoCurrency.RawID(it) },
|
||||
tokenContractAddress = type.token.contractAddress,
|
||||
value = getCurrencyAmountValue(amount) ?: return null,
|
||||
)
|
||||
is AmountType.Coin -> CryptoCurrencyAmount.Coin(
|
||||
value = getCurrencyAmountValue(amount) ?: return null,
|
||||
)
|
||||
is AmountType.FeeResource,
|
||||
AmountType.Reserve,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCurrencyTransaction(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
data: TransactionData.Uncompiled,
|
||||
): CryptoCurrencyTransaction? {
|
||||
return when (val type = data.amount.type) {
|
||||
is AmountType.Coin -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
CryptoCurrencyTransaction.Coin(txHistoryItem)
|
||||
}
|
||||
is AmountType.Token -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
CryptoCurrencyTransaction.Token(
|
||||
tokenId = type.token.id,
|
||||
tokenContractAddress = type.token.contractAddress,
|
||||
txHistoryItem = txHistoryItem,
|
||||
val value = getCurrencyAmountValue(amount) ?: return null
|
||||
|
||||
CryptoCurrencyAmount.Token(
|
||||
currencyRawId = type.token.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = type.token.contractAddress,
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
is AmountType.Coin -> {
|
||||
val value = getCurrencyAmountValue(amount) ?: return null
|
||||
|
||||
CryptoCurrencyAmount.Coin(value = value)
|
||||
}
|
||||
is AmountType.FeeResource,
|
||||
AmountType.Reserve,
|
||||
is AmountType.Reserve,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvailableAddresses(addresses: Set<SdkAddress>): Set<Address> {
|
||||
return SdkAddressToAddressConverter.convertList(addresses).toSet()
|
||||
}
|
||||
|
||||
private fun getCurrencyAmountValue(amount: Amount): BigDecimal? {
|
||||
val value = amount.value
|
||||
|
||||
|
|
@ -161,4 +130,56 @@ internal class UpdateWalletManagerResultFactory {
|
|||
|
||||
return value
|
||||
}
|
||||
|
||||
private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set<Token>): Set<CryptoCurrencyAmount> {
|
||||
val amountValue = demoAmount.value ?: BigDecimal.ZERO
|
||||
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
||||
|
||||
return tokens.mapTo(demoAmounts) { token ->
|
||||
CryptoCurrencyAmount.Token(
|
||||
currencyRawId = token.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = token.contractAddress,
|
||||
value = amountValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentTransactions(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
recentTransactions: Set<TransactionData.Uncompiled>,
|
||||
): Set<CryptoCurrencyTransaction> {
|
||||
val unconfirmedTransactions = recentTransactions.filter { it.status == TransactionStatus.Unconfirmed }
|
||||
|
||||
return unconfirmedTransactions.mapNotNullTo(hashSetOf()) {
|
||||
createCurrencyTransaction(
|
||||
txHistoryItemConverter = txHistoryItemConverter,
|
||||
data = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCurrencyTransaction(
|
||||
txHistoryItemConverter: TransactionDataToTxHistoryItemConverter,
|
||||
data: TransactionData.Uncompiled,
|
||||
): CryptoCurrencyTransaction? {
|
||||
return when (val type = data.amount.type) {
|
||||
is AmountType.Coin -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
|
||||
CryptoCurrencyTransaction.Coin(txInfo = txHistoryItem)
|
||||
}
|
||||
is AmountType.Token -> {
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
|
||||
CryptoCurrencyTransaction.Token(
|
||||
tokenId = type.token.id,
|
||||
contractAddress = type.token.contractAddress,
|
||||
txInfo = txHistoryItem,
|
||||
)
|
||||
}
|
||||
is AmountType.FeeResource,
|
||||
is AmountType.Reserve,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,786 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.*
|
||||
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult.Address.Type
|
||||
import com.tangem.common.test.domain.walletmanager.MockUpdateWalletManagerResultFactory
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import java.math.BigDecimal
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class UpdateWalletManagerResultFactoryTest {
|
||||
|
||||
private val factory = UpdateWalletManagerResultFactory()
|
||||
private val mockFactory = MockUpdateWalletManagerResultFactory()
|
||||
|
||||
private val walletManager = mockk<WalletManager>()
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(walletManager)
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getResult(model: GetResultTestModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getResult(walletManager = walletManager)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels(): List<GetResultTestModel> = listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = emptySet(),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ONE)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ONE)),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Confirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ONE),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.ZERO,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetResultTestModel(val wallet: Wallet, val expected: Verified)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetDemoResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getDemoResult(model: GetDemoResultTestModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
every { walletManager.cardTokens } returns model.cardTokens.toMutableSet()
|
||||
|
||||
// Act
|
||||
val actual = factory.getDemoResult(walletManager = walletManager, demoAmount = model.demoAmount)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels(): List<GetDemoResultTestModel> {
|
||||
return listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO), // default for demo
|
||||
),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.ONE, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ONE), // used demo amount
|
||||
),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(
|
||||
value = "0x1",
|
||||
type = Type.Primary,
|
||||
),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(
|
||||
value = "0x1",
|
||||
type = Type.Primary,
|
||||
),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.TEN)),
|
||||
currentTransactions = emptySet(),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = emptySet(),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
),
|
||||
currenciesAmounts = setOf(CryptoCurrencyAmount.Coin(value = BigDecimal.TEN)),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.TEN,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.TEN),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Confirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = null, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = setOf(usdtToken),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.ZERO,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
GetDemoResultTestModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
demoAmount = Amount(value = BigDecimal.TEN, blockchain = Blockchain.Ethereum),
|
||||
cardTokens = setOf(usdtToken),
|
||||
expected = Verified(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
currenciesAmounts = setOf(
|
||||
CryptoCurrencyAmount.Coin(value = BigDecimal.TEN),
|
||||
CryptoCurrencyAmount.Token(
|
||||
value = BigDecimal.TEN,
|
||||
currencyRawId = usdtToken.id?.let(CryptoCurrency::RawID),
|
||||
contractAddress = usdtToken.contractAddress,
|
||||
),
|
||||
),
|
||||
currentTransactions = setOf(
|
||||
CryptoCurrencyTransaction.Token(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.TEN,
|
||||
),
|
||||
tokenId = "0x3",
|
||||
contractAddress = "0x4",
|
||||
),
|
||||
CryptoCurrencyTransaction.Coin(
|
||||
txInfo = createTxInfo(
|
||||
status = TxInfo.TransactionStatus.Unconfirmed,
|
||||
amount = BigDecimal.ONE,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class GetDemoResultTestModel(
|
||||
val wallet: Wallet,
|
||||
val demoAmount: Amount,
|
||||
val cardTokens: Set<Token>,
|
||||
val expected: Verified,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetNoAccountResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getNoAccountResult(model: GetNoAccountResultModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getNoAccountResult(
|
||||
walletManager = walletManager,
|
||||
customMessage = model.customMessage,
|
||||
amountToCreateAccount = model.amountToCreateAccount,
|
||||
)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
// region amountToCreateAccount is null
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = null,
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet without amount and transactions
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
expected = mockFactory.createNoAccount(),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
customMessage = "custom message",
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
errorMessage = "custom message",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
errorMessage = "",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetNoAccountResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
customMessage = "",
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
expected = NoAccount(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
amountToCreateAccount = BigDecimal.ZERO,
|
||||
errorMessage = "",
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetNoAccountResultModel(
|
||||
val wallet: Wallet,
|
||||
val customMessage: String,
|
||||
val amountToCreateAccount: BigDecimal?,
|
||||
val expected: UpdateWalletManagerResult,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetUnreachableResult {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getUnreachableResult(model: GetUnreachableResultModel) {
|
||||
// Arrange
|
||||
every { walletManager.wallet } returns model.wallet
|
||||
|
||||
// Act
|
||||
val actual = factory.getUnreachableResult(walletManager = walletManager)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
// region Wallet without amount and transactions
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = null,
|
||||
addresses = setOf(Address(value = "0x1", type = AddressType.Default)),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and without transactions
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = emptyList(),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with coin amount and coin transaction
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(Address(value = "0x1", type = Type.Primary)),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
|
||||
// region Wallet with amounts and transactions (coin and token)
|
||||
GetUnreachableResultModel(
|
||||
wallet = createWallet(
|
||||
coinValue = BigDecimal.ONE,
|
||||
tokensAmount = mapOf(usdtToken to BigDecimal.ZERO),
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = AddressType.Default),
|
||||
Address(value = "0x11", type = AddressType.Legacy),
|
||||
),
|
||||
transactions = listOf(
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
value = BigDecimal.ONE,
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
createRecentTransaction(
|
||||
amount = Amount(
|
||||
value = BigDecimal.TEN,
|
||||
blockchain = Blockchain.Ethereum,
|
||||
type = AmountType.Token(token = usdtToken),
|
||||
currencySymbol = "USDT",
|
||||
),
|
||||
status = TransactionStatus.Unconfirmed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expected = Unreachable(
|
||||
selectedAddress = "0x1",
|
||||
addresses = setOf(
|
||||
Address(value = "0x1", type = Type.Primary),
|
||||
Address(value = "0x11", type = Type.Secondary),
|
||||
),
|
||||
),
|
||||
),
|
||||
// endregion
|
||||
)
|
||||
}
|
||||
|
||||
data class GetUnreachableResultModel(
|
||||
val wallet: Wallet,
|
||||
val expected: UpdateWalletManagerResult,
|
||||
)
|
||||
|
||||
private fun createWallet(
|
||||
coinValue: BigDecimal?,
|
||||
tokensAmount: Map<Token, BigDecimal> = emptyMap(),
|
||||
addresses: Set<Address>,
|
||||
transactions: List<TransactionData.Uncompiled>,
|
||||
): Wallet {
|
||||
return Wallet(
|
||||
blockchain = Blockchain.Ethereum,
|
||||
addresses = addresses,
|
||||
publicKey = mockk(),
|
||||
tokens = setOf(),
|
||||
).apply {
|
||||
coinValue?.let(::setCoinValue)
|
||||
tokensAmount.forEach { (token, amount) -> addTokenValue(value = amount, token = token) }
|
||||
recentTransactions += transactions
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRecentTransaction(amount: Amount, status: TransactionStatus): TransactionData.Uncompiled {
|
||||
return TransactionData.Uncompiled(
|
||||
amount = amount,
|
||||
fee = null,
|
||||
sourceAddress = "0x1",
|
||||
destinationAddress = "0x2",
|
||||
status = status,
|
||||
hash = "hash",
|
||||
date = Calendar.getInstance().apply {
|
||||
timeInMillis = 1748251839317
|
||||
},
|
||||
extras = null,
|
||||
contractAddress = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTxInfo(status: TxInfo.TransactionStatus, amount: BigDecimal): TxInfo {
|
||||
return TxInfo(
|
||||
txHash = "hash",
|
||||
timestampInMillis = 1748251839317,
|
||||
isOutgoing = true,
|
||||
destinationType = TxInfo.DestinationType.Single(
|
||||
addressType = TxInfo.AddressType.User(address = "0x2"),
|
||||
),
|
||||
sourceType = TxInfo.SourceType.Single(address = "0x1"),
|
||||
interactionAddressType = TxInfo.InteractionAddressType.User(address = "0x2"),
|
||||
status = status,
|
||||
type = TxInfo.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
val usdtToken = Token(
|
||||
id = "0x3",
|
||||
contractAddress = "0x4",
|
||||
symbol = "USDT",
|
||||
decimals = 6,
|
||||
name = "Tether",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,5 +6,6 @@ plugins {
|
|||
dependencies {
|
||||
|
||||
/* Domain */
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.managetokens.model
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
sealed class ManagedCryptoCurrency {
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.managetokens
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class CheckHasLinkedTokensUseCase(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.managetokens
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class CheckIsCurrencyNotAddedUseCase(
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.domain.managetokens
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class CreateCurrencyUseCase(
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import arrow.core.raise.either
|
|||
import arrow.core.raise.ensureNotNull
|
||||
import com.tangem.domain.managetokens.model.exceptoin.FindTokenException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class FindTokenUseCase(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import arrow.core.raise.either
|
|||
import arrow.core.raise.ensureNotNull
|
||||
import com.tangem.domain.managetokens.model.exceptoin.SupportedBlockchainException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class GetSupportedNetworksUseCase(
|
||||
|
|
|
|||
|
|
@ -5,17 +5,15 @@ import arrow.core.flatten
|
|||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
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.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
|
|
@ -24,10 +22,8 @@ class SaveManagedTokensUseCase(
|
|||
private val customTokensRepository: CustomTokensRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteFetcher: MultiQuoteFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
|
|
@ -102,20 +98,12 @@ class SaveManagedTokensUseCase(
|
|||
val networkToUpdate = currenciesToAdd.map { it.network }
|
||||
.subtract(existingCurrencies.map { it.network }.toSet())
|
||||
|
||||
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
|
||||
multiNetworkStatusFetcher(
|
||||
MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = networksToUpdate + networkToUpdate,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
networksRepository.getNetworkStatusesSync(
|
||||
multiNetworkStatusFetcher(
|
||||
MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = networksToUpdate + networkToUpdate,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshUpdatedYieldBalances(
|
||||
|
|
@ -139,19 +127,12 @@ class SaveManagedTokensUseCase(
|
|||
}
|
||||
|
||||
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
|
||||
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
|
||||
multiQuoteFetcher(
|
||||
params = MultiQuoteFetcher.Params(
|
||||
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
quotesRepository.fetchQuotes(
|
||||
multiQuoteFetcher(
|
||||
params = MultiQuoteFetcher.Params(
|
||||
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import arrow.core.raise.either
|
|||
import arrow.core.raise.ensure
|
||||
import com.tangem.domain.managetokens.model.exceptoin.DerivationPathValidationException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
class ValidateDerivationPathUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import arrow.core.raise.*
|
|||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
class ValidateTokenFormUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.managetokens.model
|
||||
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
sealed class ManageTokensUpdateAction {
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.domain.managetokens.repository
|
|||
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface CustomTokensRepository {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.domain.managetokens.model.CurrencyUnsupportedState
|
|||
import com.tangem.domain.managetokens.model.ManageTokensListBatchFlow
|
||||
import com.tangem.domain.managetokens.model.ManageTokensListBatchingContext
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface ManageTokensRepository {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ plugins {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.domain.models)
|
||||
api(projects.domain.tokens.models)
|
||||
implementation(projects.domain.core)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class TokenMarket(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import com.tangem.domain.core.serialization.SerializedBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class FilterAvailableNetworksForWalletUseCase(
|
||||
|
|
@ -25,14 +26,21 @@ class FilterAvailableNetworksForWalletUseCase(
|
|||
it.walletId == userWalletId
|
||||
} ?: return networks.toSet()
|
||||
|
||||
val supportedBlockchains = userWallet.scanResponse.card.supportedBlockchains(
|
||||
cardTypesResolver = userWallet.scanResponse.cardTypesResolver,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
val supportedBlockchains = userWallet.scanResponse.card.supportedBlockchains(
|
||||
cardTypesResolver = userWallet.scanResponse.cardTypesResolver,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
|
||||
return networks.filter {
|
||||
val blockchain = Blockchain.fromNetworkId(it.networkId)
|
||||
supportedBlockchains.contains(blockchain)
|
||||
}.toSet()
|
||||
networks.filter {
|
||||
val blockchain = Blockchain.fromNetworkId(it.networkId)
|
||||
supportedBlockchains.contains(blockchain)
|
||||
}.toSet()
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
networks.toSet() // TODO [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,21 +3,17 @@ package com.tangem.domain.markets
|
|||
import arrow.core.None
|
||||
import arrow.core.Option
|
||||
import arrow.core.toOption
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.quotes.single.SingleQuoteProducer
|
||||
import com.tangem.domain.quotes.single.SingleQuoteSupplier
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetCurrencyQuotesUseCase(
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val singleQuoteSupplier: SingleQuoteSupplier,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) {
|
||||
// TODO apply interval parameter [REDACTED_TASK_KEY]
|
||||
operator fun invoke(
|
||||
|
|
@ -27,18 +23,10 @@ class GetCurrencyQuotesUseCase(
|
|||
): Flow<Option<Quote.Value>> {
|
||||
val rawId = currencyID.rawCurrencyId ?: return flowOf(None)
|
||||
|
||||
return if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
|
||||
singleQuoteSupplier(
|
||||
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
|
||||
)
|
||||
.map { (it as? Quote.Value).toOption() }
|
||||
} else {
|
||||
quotesRepository.getQuotesUpdatesLegacy(
|
||||
currenciesIds = setOf(rawId),
|
||||
refresh = refresh,
|
||||
)
|
||||
.map { it.filterIsInstance<Quote.Value>().firstOrNull().toOption() }
|
||||
}
|
||||
return singleQuoteSupplier(
|
||||
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
|
||||
)
|
||||
.map { (it as? Quote.Value).toOption() }
|
||||
.catch { emit(None) }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.markets
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
/**
|
||||
* Get token exchanges use case
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.markets
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
class GetTokenFullQuotesUseCase(
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.markets
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.SupportedLanguages
|
||||
|
||||
class GetTokenMarketInfoUseCase(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.markets
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
class GetTokenPriceChartUseCase(
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
|
|
|
|||
|
|
@ -3,17 +3,15 @@ package com.tangem.domain.markets
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
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.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
@ -30,9 +28,7 @@ class SaveMarketTokensUseCase(
|
|||
private val derivationsRepository: DerivationsRepository,
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteFetcher: MultiQuoteFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
|
|
@ -61,7 +57,7 @@ class SaveMarketTokensUseCase(
|
|||
if (addedNetworks.isNotEmpty()) {
|
||||
derivationsRepository.derivePublicKeysByNetworkIds(
|
||||
userWalletId = userWalletId,
|
||||
networkIds = addedNetworks.map { Network.ID(it.networkId) },
|
||||
networkIds = addedNetworks.map { Network.RawID(it.networkId) },
|
||||
)
|
||||
|
||||
val addedCurrencies = addedNetworks.mapNotNull {
|
||||
|
|
@ -83,20 +79,12 @@ class SaveMarketTokensUseCase(
|
|||
}
|
||||
|
||||
private suspend fun refreshUpdatedNetworks(userWalletId: UserWalletId, addedCurrencies: List<CryptoCurrency>) {
|
||||
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
|
||||
multiNetworkStatusFetcher(
|
||||
MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
networksRepository.getNetworkStatusesSync(
|
||||
multiNetworkStatusFetcher(
|
||||
MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshUpdatedYieldBalances(
|
||||
|
|
@ -120,18 +108,11 @@ class SaveMarketTokensUseCase(
|
|||
}
|
||||
|
||||
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
|
||||
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
|
||||
multiQuoteFetcher(
|
||||
params = MultiQuoteFetcher.Params(
|
||||
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
quotesRepository.fetchQuotes(
|
||||
multiQuoteFetcher(
|
||||
params = MultiQuoteFetcher.Params(
|
||||
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.markets.repositories
|
||||
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
|
|
@ -1,32 +1,34 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
package com.tangem.domain.models.currency
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents a generic cryptocurrency.
|
||||
*
|
||||
* @property id Unique identifier for the cryptocurrency.
|
||||
* @property network The network to which the cryptocurrency belongs.
|
||||
* @property name Human-readable name of the cryptocurrency.
|
||||
* @property symbol Symbol of the cryptocurrency.
|
||||
* @property decimals Number of decimal places used by the cryptocurrency.
|
||||
* @property iconUrl Optional URL of the cryptocurrency icon. `null` if not found.
|
||||
* @property isCustom Indicates whether the currency is a custom user-added currency or not.
|
||||
*/
|
||||
/** Represents a generic cryptocurrency */
|
||||
@Serializable
|
||||
sealed class CryptoCurrency {
|
||||
|
||||
/** Unique identifier for the cryptocurrency */
|
||||
abstract val id: ID
|
||||
|
||||
/** The network to which the cryptocurrency belongs */
|
||||
abstract val network: Network
|
||||
|
||||
/** Human-readable name of the cryptocurrency */
|
||||
abstract val name: String
|
||||
|
||||
/** Symbol of the cryptocurrency */
|
||||
abstract val symbol: String
|
||||
|
||||
/** Number of decimal places used by the cryptocurrency */
|
||||
abstract val decimals: Int
|
||||
|
||||
/** Optional URL of the cryptocurrency icon. `null` if not found. */
|
||||
abstract val iconUrl: String?
|
||||
|
||||
/** Indicates whether the currency is a custom user-added currency or not */
|
||||
abstract val isCustom: Boolean
|
||||
|
||||
/**
|
||||
* Represents a native coin in the blockchain network.
|
||||
*/
|
||||
/** Represents a native coin in the blockchain network */
|
||||
@Serializable
|
||||
data class Coin(
|
||||
override val id: ID,
|
||||
|
|
@ -44,9 +46,9 @@ sealed class CryptoCurrency {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents a token in the blockchain network, typically a non-native asset.
|
||||
* Represents a token in the blockchain network, typically a non-native asset
|
||||
*
|
||||
* @property contractAddress Address of the contract managing the token.
|
||||
* @property contractAddress address of the contract managing the token
|
||||
*/
|
||||
@Serializable
|
||||
data class Token(
|
||||
|
|
@ -72,9 +74,9 @@ sealed class CryptoCurrency {
|
|||
* The ID is designed to ensure that different cryptocurrencies, whether they are standard tokens, custom tokens or
|
||||
* standard coins, can be distinctly identified within a system.
|
||||
*
|
||||
* @property value Constructed unique identifier value, made up of prefix, network ID, and suffix.
|
||||
* @property rawCurrencyId Represents not unique currency ID from the blockchain network. `null` if
|
||||
* its ID of the custom token.
|
||||
* @property prefix prefix
|
||||
* @property body body
|
||||
* @property suffix suffix
|
||||
*/
|
||||
@Serializable
|
||||
data class ID(
|
||||
|
|
@ -83,6 +85,7 @@ sealed class CryptoCurrency {
|
|||
private val suffix: Suffix,
|
||||
) {
|
||||
|
||||
/** Constructed unique identifier value, made up of prefix, network ID, and suffix */
|
||||
val value: String
|
||||
get() = buildString {
|
||||
append(prefix.value)
|
||||
|
|
@ -92,12 +95,13 @@ sealed class CryptoCurrency {
|
|||
append(suffix.value)
|
||||
}
|
||||
|
||||
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */
|
||||
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null` */
|
||||
val rawCurrencyId: RawID? get() = (suffix as? Suffix.RawID)?.rawId?.let { RawID(it) }
|
||||
|
||||
/** Represents a contract address */
|
||||
val contractAddress: String? get() = (suffix as? Suffix.RawID)?.contractAddress
|
||||
|
||||
/** Represents a raw cryptocurrency's network ID. */
|
||||
/** Represents a raw cryptocurrency's network ID */
|
||||
val rawNetworkId: String
|
||||
get() = when (body) {
|
||||
is Body.NetworkId -> body.rawId
|
||||
|
|
@ -106,10 +110,10 @@ sealed class CryptoCurrency {
|
|||
|
||||
/**
|
||||
* Represents the different types of prefixes that can be associated with a cryptocurrency ID.
|
||||
*
|
||||
* These prefixes can help in quickly categorizing the type of cryptocurrency.
|
||||
*/
|
||||
enum class Prefix(val value: String) {
|
||||
|
||||
/** Prefix for standard coins. */
|
||||
COIN_PREFIX(value = "coin"),
|
||||
|
||||
|
|
@ -119,7 +123,6 @@ sealed class CryptoCurrency {
|
|||
|
||||
/**
|
||||
* Represents the body part of the cryptocurrency ID.
|
||||
*
|
||||
* The body can be either a raw network ID or a raw network ID with a network derivation path.
|
||||
*/
|
||||
@Serializable
|
||||
|
|
@ -136,9 +139,8 @@ sealed class CryptoCurrency {
|
|||
|
||||
/**
|
||||
* Represents a raw network ID with a network derivation path.
|
||||
*
|
||||
* Should be used for a cryptocurrencies with custom derivation path.
|
||||
* */
|
||||
*/
|
||||
@Serializable
|
||||
data class NetworkIdWithDerivationPath(
|
||||
val rawId: String,
|
||||
|
|
@ -161,7 +163,6 @@ sealed class CryptoCurrency {
|
|||
|
||||
/**
|
||||
* Represents the suffix part of the cryptocurrency ID.
|
||||
*
|
||||
* The suffix can either be a raw ID or a contract address.
|
||||
*/
|
||||
@Serializable
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.models.network
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
/**
|
||||
* Crypto currency address
|
||||
*
|
||||
* @property cryptoCurrency crypto currency
|
||||
* @property address default address
|
||||
*/
|
||||
data class CryptoCurrencyAddress(val cryptoCurrency: CryptoCurrency, val address: String)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
package com.tangem.domain.models.network
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -9,17 +9,17 @@ import kotlinx.serialization.Serializable
|
|||
* whether it operates as a test network, and the type of blockchain standard it conforms to
|
||||
* (e.g., ERC20, BEP20).
|
||||
*
|
||||
* @property id The unique identifier of the network.
|
||||
* @property backendId The name of this network in the Tangem backend.
|
||||
* @property name The human-readable name of the network, such as "Ethereum" or "Bitcoin".
|
||||
* @property derivationPath The path used to derive keys for this network.
|
||||
* @property isTestnet Indicates whether the network is a test network or a main network.
|
||||
* @property standardType The type of blockchain standard the network adheres to.
|
||||
* @property hasFiatFeeRate Indicates whether there is a fee in the network
|
||||
* that cannot be represented in a fiat currency.
|
||||
* (For those blockchains that have FeeResource instead of a standard type of fee)
|
||||
* @property canHandleTokens Indicates whether the network can handle tokens.
|
||||
* @property transactionExtrasType The type of extras supported for sending a transaction.
|
||||
* @property id the unique identifier of the network
|
||||
* @property backendId the name of this network in the Tangem backend
|
||||
* @property name the human-readable name of the network, such as "Ethereum" or "Bitcoin"
|
||||
* @property currencySymbol the symbol of the currency associated with the network
|
||||
* @property derivationPath the path used to derive keys for this network
|
||||
* @property isTestnet indicates whether the network is a test network or a main network
|
||||
* @property standardType the type of blockchain standard the network adheres to
|
||||
* @property hasFiatFeeRate indicates whether there is a fee in the network that cannot be represented in a fiat
|
||||
* currency (for those blockchains that have FeeResource instead of a standard type of fee)
|
||||
* @property canHandleTokens indicates whether the network can handle tokens
|
||||
* @property transactionExtrasType the type of extras supported for sending a transaction
|
||||
*/
|
||||
@Serializable
|
||||
data class Network(
|
||||
|
|
@ -35,55 +35,60 @@ data class Network(
|
|||
val transactionExtrasType: TransactionExtrasType,
|
||||
) {
|
||||
|
||||
/** Raw ID */
|
||||
val rawId: String
|
||||
get() = id.rawId.value
|
||||
|
||||
init {
|
||||
require(name.isNotBlank()) { "Network name must not be blank" }
|
||||
require(id.derivationPath == derivationPath) { "Derivation path must be the same as in the ID" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a unique identifier for a blockchain network.
|
||||
* Represents a unique identifier for a blockchain network
|
||||
*
|
||||
* @property value The string representation of the network ID.
|
||||
* @property rawId raw network ID
|
||||
* @property derivationPath derivation path
|
||||
*/
|
||||
@JvmInline
|
||||
@Serializable
|
||||
value class ID(val value: String) {
|
||||
data class ID(val rawId: RawID, val derivationPath: DerivationPath) {
|
||||
|
||||
init {
|
||||
require(value.isNotBlank()) { "Network ID must not be blank" }
|
||||
require(rawId.value.isNotBlank()) { "Network ID must not be blank" }
|
||||
}
|
||||
|
||||
constructor(value: String, derivationPath: DerivationPath) : this(
|
||||
rawId = RawID(value),
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RawID(val value: String) {
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a path used to derive cryptographic keys for a blockchain network.
|
||||
*
|
||||
* This class represents such paths in a generic manner, allowing for predefined card-based paths,
|
||||
* custom paths, or even no derivation path at all.
|
||||
* This class represents such paths in a generic manner, allowing for predefined card-based paths, custom paths,
|
||||
* or even no derivation path at all.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class DerivationPath {
|
||||
|
||||
/** The actual derivation path value, if any. */
|
||||
/** The actual derivation path value, if any */
|
||||
abstract val value: String?
|
||||
|
||||
/**
|
||||
* Represents a predefined card-based derivation path.
|
||||
*
|
||||
* @property value The derivation path string.
|
||||
*/
|
||||
/** Represents a predefined card-based derivation path [value] */
|
||||
@Serializable
|
||||
data class Card(override val value: String) : DerivationPath()
|
||||
|
||||
/**
|
||||
* Represents a custom derivation path specified by the user.
|
||||
*
|
||||
* @property value The derivation path string.
|
||||
*/
|
||||
/** Represents a custom derivation path [value] specified by the user */
|
||||
@Serializable
|
||||
data class Custom(override val value: String) : DerivationPath()
|
||||
|
||||
/**
|
||||
* Represents a lack of derivation path, which means the wallet does not support the HD wallet feature.
|
||||
*/
|
||||
/** Represents a lack of derivation path, which means the wallet does not support the HD wallet feature */
|
||||
@Serializable
|
||||
data object None : DerivationPath() {
|
||||
override val value: String? get() = null
|
||||
|
|
@ -96,45 +101,43 @@ data class Network(
|
|||
* Blockchain networks often follow certain standards that dictate how tokens operate on them.
|
||||
* These standards can define functionalities such as how transactions are processed,
|
||||
* how tokens are minted or burned, and more.
|
||||
*
|
||||
* @property name The human-readable name of the standard type.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class StandardType {
|
||||
|
||||
/** The human-readable name of the standard type */
|
||||
abstract val name: String
|
||||
|
||||
/** Represents the ERC20 token standard, common on the Ethereum network. */
|
||||
/** Represents the ERC20 token standard, common on the Ethereum network */
|
||||
@Serializable
|
||||
data object ERC20 : StandardType() {
|
||||
override val name: String get() = "ERC20"
|
||||
}
|
||||
|
||||
/** Represents the TRC20 token standard, common on the TRON network. */
|
||||
/** Represents the TRC20 token standard, common on the TRON network */
|
||||
@Serializable
|
||||
data object TRC20 : StandardType() {
|
||||
override val name: String get() = "TRC20"
|
||||
}
|
||||
|
||||
/** Represents the BEP20 token standard, common on the Binance Smart Chain network. */
|
||||
/** Represents the BEP20 token standard, common on the Binance Smart Chain network */
|
||||
@Serializable
|
||||
data object BEP20 : StandardType() {
|
||||
override val name: String get() = "BEP20"
|
||||
}
|
||||
|
||||
/** Represents the BEP2 token standard, common on the Binance Chain network. */
|
||||
/** Represents the BEP2 token standard, common on the Binance Chain network */
|
||||
@Serializable
|
||||
data object BEP2 : StandardType() {
|
||||
override val name: String get() = "BEP2"
|
||||
}
|
||||
|
||||
/** Represents a network that does not adhere to a predefined standard type. */
|
||||
/** Represents a network that does not adhere to a predefined standard type */
|
||||
@Serializable
|
||||
data class Unspecified(override val name: String) : StandardType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the supported type of extras for sending a transaction.
|
||||
* */
|
||||
/** Represents the supported type of extras for sending a transaction */
|
||||
enum class TransactionExtrasType {
|
||||
|
||||
/** No transaction extras supported */
|
||||
|
|
@ -1,20 +1,18 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
package com.tangem.domain.models.network
|
||||
|
||||
/**
|
||||
* Represents a network address configuration.
|
||||
*/
|
||||
/** Represents a network address */
|
||||
sealed class NetworkAddress {
|
||||
|
||||
/** The default or currently selected network address. */
|
||||
/** The default or currently selected network address */
|
||||
abstract val defaultAddress: Address
|
||||
|
||||
/** The set of available network addresses to choose from. */
|
||||
/** The set of available network addresses to choose from */
|
||||
abstract val availableAddresses: Set<Address>
|
||||
|
||||
/**
|
||||
* Represents a single static network address.
|
||||
* Represents a single static network address
|
||||
*
|
||||
* @property defaultAddress The static network address.
|
||||
* @property defaultAddress the static network address
|
||||
*/
|
||||
data class Single(override val defaultAddress: Address) : NetworkAddress() {
|
||||
|
||||
|
|
@ -22,10 +20,10 @@ sealed class NetworkAddress {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents a network configuration where an address can be chosen from a set of available addresses.
|
||||
* Represents a network configuration where an address can be chosen from a set of available addresses
|
||||
*
|
||||
* @property defaultAddress The currently selected or default network address.
|
||||
* @property availableAddresses The set of available network addresses to choose from.
|
||||
* @property defaultAddress the currently selected or default network address
|
||||
* @property availableAddresses the set of available network addresses to choose from
|
||||
*/
|
||||
data class Selectable(
|
||||
override val defaultAddress: Address,
|
||||
|
|
@ -37,6 +35,12 @@ sealed class NetworkAddress {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Address
|
||||
*
|
||||
* @property value string representation of the address
|
||||
* @property type address type
|
||||
*/
|
||||
data class Address(
|
||||
val value: String,
|
||||
val type: Type,
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.domain.models.network
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Represents the status of a specific blockchain network
|
||||
*
|
||||
* @property network the network for which the status is provided
|
||||
* @property value the specific status value, represented as a sealed class to encapsulate the various possible
|
||||
* states of the network
|
||||
*/
|
||||
data class NetworkStatus(val network: Network, val value: Value) {
|
||||
|
||||
/** Represents the various possible statuses of a network */
|
||||
sealed class Value {
|
||||
|
||||
/** Status source */
|
||||
abstract val source: StatusSource
|
||||
|
||||
fun copySealed(source: StatusSource): Value {
|
||||
return when (this) {
|
||||
is NoAccount -> copy(source = source)
|
||||
is Verified -> copy(source = source)
|
||||
is Unreachable,
|
||||
is MissedDerivation,
|
||||
-> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state where the network is unreachable
|
||||
*
|
||||
* @property address network address
|
||||
*/
|
||||
data class Unreachable(val address: NetworkAddress?) : Value() {
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
}
|
||||
|
||||
/** Represents the state where a derivation has been missed */
|
||||
data object MissedDerivation : Value() {
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the verified state of the network, including the amounts associated with different cryptocurrencies
|
||||
* and whether there are transactions in progress
|
||||
*
|
||||
* @property address network address
|
||||
* @property amounts a map containing the amounts associated with different cryptocurrencies within the
|
||||
* network
|
||||
* @property pendingTransactions a map containing pending transactions associated with different cryptocurrencies
|
||||
* @property source source of data
|
||||
*/
|
||||
data class Verified(
|
||||
val address: NetworkAddress,
|
||||
val amounts: Map<CryptoCurrency.ID, Amount>,
|
||||
val pendingTransactions: Map<CryptoCurrency.ID, Set<TxInfo>>,
|
||||
override val source: StatusSource,
|
||||
) : Value()
|
||||
|
||||
/**
|
||||
* Represents the state where there is no account, and an amount is required to create one
|
||||
*
|
||||
* @property address network address
|
||||
* @property amountToCreateAccount the amount required to create an account within the network
|
||||
* @property errorMessage error message
|
||||
* @property source source of data
|
||||
*/
|
||||
data class NoAccount(
|
||||
val address: NetworkAddress,
|
||||
val amountToCreateAccount: BigDecimal,
|
||||
val errorMessage: String,
|
||||
override val source: StatusSource,
|
||||
) : Value()
|
||||
|
||||
/** Represents possible statuses of cryptocurrency amount */
|
||||
sealed interface Amount {
|
||||
|
||||
/**
|
||||
* Loaded amount
|
||||
*
|
||||
* @property value amount value
|
||||
*/
|
||||
data class Loaded(val value: BigDecimal) : Amount
|
||||
|
||||
/** Amount which failed to load */
|
||||
data object NotFound : Amount
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,21 @@
|
|||
package com.tangem.domain.txhistory.models
|
||||
package com.tangem.domain.models.network
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class TxHistoryItem(
|
||||
/**
|
||||
* Represents information about a transaction. Do not use it for sending transactions.
|
||||
*
|
||||
* @property txHash transaction hash
|
||||
* @property timestampInMillis transaction timestamp in milliseconds
|
||||
* @property isOutgoing flag that determines the direction of the transaction (incoming or outgoing)
|
||||
* @property destinationType type of destination (single or multiple)
|
||||
* @property sourceType type of source (single or multiple)
|
||||
* @property interactionAddressType interaction address type
|
||||
* @property status transaction status
|
||||
* @property type transaction type
|
||||
* @property amount transaction amount
|
||||
*/
|
||||
data class TxInfo(
|
||||
val txHash: String,
|
||||
val timestampInMillis: Long,
|
||||
val isOutgoing: Boolean,
|
||||
|
|
@ -14,18 +27,28 @@ data class TxHistoryItem(
|
|||
val amount: BigDecimal,
|
||||
) {
|
||||
|
||||
/** Destination type*/
|
||||
sealed class DestinationType {
|
||||
|
||||
/**
|
||||
* Single
|
||||
*
|
||||
* @property addressType address type
|
||||
*/
|
||||
data class Single(val addressType: AddressType) : DestinationType()
|
||||
|
||||
/**
|
||||
* Multiple
|
||||
*
|
||||
* @property addressTypes addresses types
|
||||
*/
|
||||
data class Multiple(val addressTypes: List<AddressType>) : DestinationType()
|
||||
}
|
||||
|
||||
sealed class SourceType {
|
||||
|
||||
data class Single(val address: String) : SourceType()
|
||||
data class Multiple(val addresses: List<String>) : SourceType()
|
||||
}
|
||||
|
||||
/** Address type */
|
||||
sealed class AddressType {
|
||||
|
||||
/** Address value */
|
||||
abstract val address: String
|
||||
|
||||
data class User(override val address: String) : AddressType()
|
||||
|
|
@ -33,6 +56,25 @@ data class TxHistoryItem(
|
|||
data class Validator(override val address: String) : AddressType()
|
||||
}
|
||||
|
||||
/** Source type */
|
||||
sealed class SourceType {
|
||||
|
||||
/**
|
||||
* Single
|
||||
*
|
||||
* @property address address
|
||||
*/
|
||||
data class Single(val address: String) : SourceType()
|
||||
|
||||
/**
|
||||
* Multiple
|
||||
*
|
||||
* @property addresses addresses
|
||||
*/
|
||||
data class Multiple(val addresses: List<String>) : SourceType()
|
||||
}
|
||||
|
||||
/** Transaction type */
|
||||
sealed interface TransactionType {
|
||||
data object Transfer : TransactionType
|
||||
data object Approve : TransactionType
|
||||
|
|
@ -50,6 +92,7 @@ data class TxHistoryItem(
|
|||
}
|
||||
}
|
||||
|
||||
/** Transaction status */
|
||||
sealed class TransactionStatus {
|
||||
data object Failed : TransactionStatus()
|
||||
data object Unconfirmed : TransactionStatus()
|
||||
|
|
@ -5,6 +5,6 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
api(projects.domain.core)
|
||||
api(projects.domain.tokens.models)
|
||||
api(projects.domain.models)
|
||||
api(projects.domain.wallets.models)
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.domain.networks.multi
|
||||
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Fetcher of network status [Network] for wallet with [UserWalletId]
|
||||
* Fetcher of network status [Network] for multi-currency wallet with [UserWalletId]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.networks.multi
|
||||
|
||||
import com.tangem.domain.core.flow.FlowProducer
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.networks.multi
|
||||
|
||||
import com.tangem.domain.core.flow.FlowCachingSupplier
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
|
||||
/**
|
||||
* Supplier of all networks statuses for selected wallet [MultiNetworkStatusProducer.Params]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.networks.repository
|
||||
|
||||
import com.tangem.domain.models.network.CryptoCurrencyAddress
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Repository for working with pending transactions
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface NetworksRepository {
|
||||
|
||||
/** Fetches pending transactions for given [network] in selected [userWalletId] */
|
||||
suspend fun fetchPendingTransactions(userWalletId: UserWalletId, network: Network)
|
||||
|
||||
/**
|
||||
* Returns addresses and crypto currency
|
||||
*
|
||||
* @param userWalletId the unique identifier of the user wallet
|
||||
* @param network network
|
||||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List<CryptoCurrencyAddress>
|
||||
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network.RawID): List<CryptoCurrencyAddress>
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.domain.networks.single
|
||||
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
@ -12,18 +11,11 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
*/
|
||||
interface SingleNetworkStatusFetcher : FlowFetcher<SingleNetworkStatusFetcher.Params> {
|
||||
|
||||
/** Params */
|
||||
sealed interface Params {
|
||||
|
||||
val userWalletId: UserWalletId
|
||||
val network: Network
|
||||
|
||||
data class Simple(override val userWalletId: UserWalletId, override val network: Network) : Params
|
||||
|
||||
data class Prepared(
|
||||
override val userWalletId: UserWalletId,
|
||||
override val network: Network,
|
||||
val addedNetworkCurrencies: Set<CryptoCurrency>,
|
||||
) : Params
|
||||
}
|
||||
/**
|
||||
* Params
|
||||
*
|
||||
* @property userWalletId user wallet id
|
||||
* @property network network
|
||||
*/
|
||||
data class Params(val userWalletId: UserWalletId, val network: Network)
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.domain.networks.single
|
||||
|
||||
import com.tangem.domain.core.flow.FlowProducer
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.networks.single
|
||||
|
||||
import com.tangem.domain.core.flow.FlowCachingSupplier
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
|
||||
/**
|
||||
* Supplier of network status for selected wallet [SingleNetworkStatusProducer.Params]
|
||||
|
|
|
|||
|
|
@ -10,16 +10,25 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.kotlin.coroutines)
|
||||
|
||||
// region Project – Core
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.utils)
|
||||
// endregion
|
||||
|
||||
// region Project – Domain
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.networks)
|
||||
implementation(projects.domain.nft.models)
|
||||
implementation(projects.domain.quotes)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
// endregion
|
||||
|
||||
// region Others
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.kotlin.coroutines)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.domain.nft.models
|
|||
|
||||
import com.tangem.domain.core.serialization.SerializedBigInteger
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.nft.models
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
@ -41,7 +41,7 @@ data class NFTCollection(
|
|||
data class TON(val contractAddress: String?) : Identifier()
|
||||
|
||||
@Serializable
|
||||
data class Solana(val collection: String?) : Identifier()
|
||||
data class Solana(val collectionAddress: String?) : Identifier()
|
||||
|
||||
@Serializable
|
||||
data object Unknown : Identifier()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.nft.models
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
data class NFTCollections(
|
||||
val network: Network,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.nft.models
|
||||
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
data class NFTNetworks(
|
||||
val availableNetworks: List<Network>,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ sealed class NFTSalePrice {
|
|||
data class Value(
|
||||
override val assetId: NFTAsset.Identifier,
|
||||
val value: SerializedBigDecimal,
|
||||
val fiatValue: SerializedBigDecimal?,
|
||||
val symbol: String,
|
||||
val decimals: Int,
|
||||
) : NFTSalePrice()
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
||||
class DisableWalletNFTUseCase(
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val nftRepository: NFTRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId) {
|
||||
walletsRepository.disableNFT(userWalletId)
|
||||
|
||||
val currencies = currenciesRepository.getMultiCurrencyWalletCachedCurrenciesSync(userWalletId)
|
||||
val networks = currencies.map { it.network }
|
||||
nftRepository.clearCache(userWalletId, networks)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
||||
class EnableWalletNFTUseCase(
|
||||
private val walletsRepository: WalletsRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId) {
|
||||
walletsRepository.enableNFT(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class FetchNFTCollectionAssetsUseCase(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.quotes.single.SingleQuoteFetcher
|
||||
|
||||
class FetchNFTPriceUseCase(
|
||||
private val nftRepository: NFTRepository,
|
||||
private val singleQuoteFetcher: SingleQuoteFetcher,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(network: Network, appCurrencyId: String?): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
val nftCurrency = nftRepository.getNFTCurrency(network)
|
||||
val rawId = nftCurrency.id.rawCurrencyId ?: error("Invalid nft currency id")
|
||||
|
||||
singleQuoteFetcher(
|
||||
params = SingleQuoteFetcher.Params(
|
||||
rawCurrencyId = rawId,
|
||||
appCurrencyId = appCurrencyId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.models.NFTNetworks
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import arrow.core.raise.catch
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
class GetNFTExploreUrlUseCase(
|
||||
private val nftRepository: NFTRepository,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
class GetNFTNetworkStatusUseCase(
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): NetworkStatus? = networksRepository
|
||||
.getNetworkStatusesSync(userWalletId, setOf(network), false)
|
||||
.firstOrNull { it.network == network }
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): NetworkStatus? {
|
||||
return singleNetworkStatusSupplier(
|
||||
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = network),
|
||||
)
|
||||
.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.quotes.single.SingleQuoteProducer
|
||||
import com.tangem.domain.quotes.single.SingleQuoteSupplier
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetNFTPriceUseCase(
|
||||
private val nftRepository: NFTRepository,
|
||||
private val singleQuoteSupplier: SingleQuoteSupplier,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, nftAsset: NFTAsset): Either<Throwable, Flow<NFTSalePrice>> {
|
||||
return Either.catch {
|
||||
val nftCurrency = nftRepository.getNFTCurrency(nftAsset.network)
|
||||
val rawId = nftCurrency.id.rawCurrencyId ?: error("Invalid nft currency id")
|
||||
|
||||
singleQuoteSupplier(
|
||||
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
|
||||
).map { quote ->
|
||||
val nftPrice = nftRepository.getNFTSalePrice(
|
||||
userWalletId = userWalletId,
|
||||
network = nftAsset.network,
|
||||
collectionId = nftAsset.collectionId,
|
||||
assetId = nftAsset.id,
|
||||
)
|
||||
|
||||
val quoteValue = quote as? Quote.Value
|
||||
|
||||
if (nftPrice !is NFTSalePrice.Value) {
|
||||
nftPrice
|
||||
} else {
|
||||
nftPrice.copy(fiatValue = quoteValue?.fiatRate?.multiply(nftPrice.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class GetWalletNFTEnabledUseCase(
|
||||
private val walletsRepository: WalletsRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<Boolean> = walletsRepository
|
||||
.nftEnabledStatus(userWalletId)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.domain.nft
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
class ObserveAndClearNFTCacheIfNeedUseCase(
|
||||
private val nftRepository: NFTRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
) {
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<Set<Network>> = currenciesRepository
|
||||
.getWalletCurrenciesUpdates(userWalletId)
|
||||
.map { it.map(CryptoCurrency::network) }
|
||||
.mapDiff { old, new ->
|
||||
// calculate networks sets difference to determine which networks were removed
|
||||
old.toSet() - new.toSet()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach { removedNetworks ->
|
||||
if (removedNetworks.isNotEmpty()) {
|
||||
nftRepository.clearCache(userWalletId, removedNetworks.toList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T, R> Flow<T>.mapDiff(diff: (old: T, new: T) -> R): Flow<R> = flow {
|
||||
var previous: T? = null
|
||||
collect { current ->
|
||||
val prev = previous
|
||||
if (prev != null) {
|
||||
emit(diff(prev, current))
|
||||
}
|
||||
previous = current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,26 @@
|
|||
package com.tangem.domain.nft.repository
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.models.NFTCollections
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface NFTRepository {
|
||||
fun observeCollections(userWalletId: UserWalletId, networks: List<Network>): Flow<List<NFTCollections>>
|
||||
|
||||
fun getNFTCurrency(network: Network): CryptoCurrency
|
||||
|
||||
suspend fun getNFTSalePrice(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
collectionId: NFTCollection.Identifier,
|
||||
assetId: NFTAsset.Identifier,
|
||||
): NFTSalePrice
|
||||
|
||||
suspend fun refreshCollections(userWalletId: UserWalletId, networks: List<Network>)
|
||||
|
||||
suspend fun refreshAssets(userWalletId: UserWalletId, network: Network, collectionId: NFTCollection.Identifier)
|
||||
|
|
@ -21,4 +32,6 @@ interface NFTRepository {
|
|||
suspend fun getNFTSupportedNetworks(userWalletId: UserWalletId): List<Network>
|
||||
|
||||
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
|
||||
|
||||
suspend fun clearCache(userWalletId: UserWalletId, networks: List<Network>)
|
||||
}
|
||||
|
|
@ -13,10 +13,10 @@ android {
|
|||
dependencies {
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.notifications.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.notifications.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.libs.crypto)
|
||||
|
||||
// region DI
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
package com.tangem.domain.notifications.models
|
||||
|
||||
@JvmInline
|
||||
value class ApplicationId(val value: String)
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.notifications.models
|
||||
|
||||
enum class NotificationType(val type: String) {
|
||||
Promo("promo"),
|
||||
IncomeTransactions("income_transaction"),
|
||||
SwapStatus("swap_status_update"),
|
||||
OnrampStatus("onramp_status_update"),
|
||||
Unknown("unknown"),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun getType(type: String?): NotificationType {
|
||||
return entries.firstOrNull { it.type == type } ?: Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.notifications.models
|
|||
|
||||
data class NotificationsEligibleNetwork(
|
||||
val id: String,
|
||||
val networkId: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val icon: Int,
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.notifications
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -10,7 +11,7 @@ class GetApplicationIdUseCase(
|
|||
) {
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, String> = Either.catch {
|
||||
suspend operator fun invoke(): Either<Throwable, ApplicationId> = Either.catch {
|
||||
val localApplicationId = notificationsRepository.getApplicationId()
|
||||
if (localApplicationId != null) return@catch localApplicationId
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.domain.notifications
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
|
||||
class GetNetworksAvailableForNotificationsUseCase(
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, List<NotificationsEligibleNetwork>> = Either.catch {
|
||||
notificationsRepository.getEligibleNetworks()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.notifications
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
|
||||
class IncrementNotificationsShowCountUseCase(
|
||||
|
|
@ -10,7 +10,7 @@ class IncrementNotificationsShowCountUseCase(
|
|||
|
||||
suspend operator fun invoke(cryptoCurrency: CryptoCurrency) {
|
||||
val isTronToken = cryptoCurrency is CryptoCurrency.Token &&
|
||||
isTron(cryptoCurrency.network.id.value)
|
||||
isTron(cryptoCurrency.network.rawId)
|
||||
|
||||
if (isTronToken) {
|
||||
notificationsRepository.incrementTronTokenFeeNotificationShowCounter()
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
package com.tangem.domain.notifications
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.utils.notifications.PushNotificationsTokenProvider
|
||||
|
||||
class SendPushTokenUseCase(
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val getApplicationIdUseCase: GetApplicationIdUseCase,
|
||||
private val pushNotificationsTokenProvider: PushNotificationsTokenProvider,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, Unit> = Either.catch {
|
||||
val applicationId = getApplicationIdUseCase().getOrNull()
|
||||
?: error("Application ID not found")
|
||||
suspend operator fun invoke(applicationId: ApplicationId): Either<Throwable, Unit> = Either.catch {
|
||||
val token = pushNotificationsTokenProvider.getToken()
|
||||
notificationsRepository.sendPushToken(applicationId, token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,23 @@
|
|||
package com.tangem.domain.notifications.repository
|
||||
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
|
||||
|
||||
interface NotificationsRepository {
|
||||
|
||||
@Throws
|
||||
suspend fun createApplicationId(pushToken: String? = null): String
|
||||
suspend fun createApplicationId(pushToken: String? = null): ApplicationId
|
||||
|
||||
suspend fun saveApplicationId(appId: String)
|
||||
suspend fun saveApplicationId(appId: ApplicationId)
|
||||
|
||||
suspend fun getApplicationId(): String?
|
||||
suspend fun getApplicationId(): ApplicationId?
|
||||
|
||||
suspend fun getTronTokenFeeNotificationShowCounter(): Int
|
||||
|
||||
suspend fun incrementTronTokenFeeNotificationShowCounter()
|
||||
|
||||
@Throws
|
||||
suspend fun associateApplicationIdWithWallets(appId: String, wallets: List<String>)
|
||||
|
||||
@Throws
|
||||
suspend fun setWalletName(walletId: String, walletName: String)
|
||||
|
||||
@Throws
|
||||
suspend fun getWalletName(walletId: String): String?
|
||||
|
||||
@Throws
|
||||
suspend fun sendPushToken(appId: String, pushToken: String)
|
||||
suspend fun sendPushToken(appId: ApplicationId, pushToken: String)
|
||||
|
||||
@Throws
|
||||
suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.notifications
|
|||
|
||||
import arrow.core.Either
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
|
|
@ -20,7 +21,7 @@ class GetApplicationIdUseCaseTest {
|
|||
@Test
|
||||
fun `GIVEN local application ID exists WHEN invoke THEN return local application ID`() = runTest {
|
||||
// GIVEN
|
||||
val expectedApplicationId = "test-app-id"
|
||||
val expectedApplicationId = ApplicationId("test-app-id")
|
||||
coEvery { notificationsRepository.getApplicationId() } returns expectedApplicationId
|
||||
|
||||
// WHEN
|
||||
|
|
@ -39,7 +40,7 @@ class GetApplicationIdUseCaseTest {
|
|||
@Test
|
||||
fun `GIVEN local application ID does not exist WHEN invoke THEN create and save new application ID`() = runTest {
|
||||
// GIVEN
|
||||
val newApplicationId = "new-app-id"
|
||||
val newApplicationId = ApplicationId("new-app-id")
|
||||
coEvery { notificationsRepository.getApplicationId() } returns null
|
||||
coEvery { notificationsRepository.createApplicationId() } returns newApplicationId
|
||||
coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit
|
||||
|
|
@ -81,7 +82,7 @@ class GetApplicationIdUseCaseTest {
|
|||
fun `GIVEN no local application ID WHEN multiple concurrent invokes THEN create only one application ID`() =
|
||||
runTest {
|
||||
// GIVEN
|
||||
val newApplicationId = "new-app-id"
|
||||
val newApplicationId = ApplicationId("new-app-id")
|
||||
var isIdCreated = false
|
||||
|
||||
coEvery { notificationsRepository.getApplicationId() } answers {
|
||||
|
|
@ -115,7 +116,7 @@ class GetApplicationIdUseCaseTest {
|
|||
@Test
|
||||
fun `GIVEN no local application ID WHEN multiple concurrent invokes with delay THEN create only one application ID`() = runTest {
|
||||
// GIVEN
|
||||
val newApplicationId = "new-app-id"
|
||||
val newApplicationId = ApplicationId("new-app-id")
|
||||
var isIdCreated = false
|
||||
|
||||
coEvery { notificationsRepository.getApplicationId() } answers {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.notifications
|
|||
|
||||
import arrow.core.Either
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.notifications.models.ApplicationId
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.utils.notifications.PushNotificationsTokenProvider
|
||||
import io.mockk.coEvery
|
||||
|
|
@ -14,18 +15,15 @@ import org.junit.Test
|
|||
class SendPushTokenUseCaseTest {
|
||||
|
||||
private lateinit var notificationsRepository: NotificationsRepository
|
||||
private lateinit var getApplicationIdUseCase: GetApplicationIdUseCase
|
||||
private lateinit var pushNotificationsTokenProvider: PushNotificationsTokenProvider
|
||||
private lateinit var sendPushTokenUseCase: SendPushTokenUseCase
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
notificationsRepository = mockk()
|
||||
getApplicationIdUseCase = mockk()
|
||||
pushNotificationsTokenProvider = mockk()
|
||||
sendPushTokenUseCase = SendPushTokenUseCase(
|
||||
notificationsRepository = notificationsRepository,
|
||||
getApplicationIdUseCase = getApplicationIdUseCase,
|
||||
pushNotificationsTokenProvider = pushNotificationsTokenProvider,
|
||||
)
|
||||
}
|
||||
|
|
@ -33,44 +31,30 @@ class SendPushTokenUseCaseTest {
|
|||
@Test
|
||||
fun `GIVEN valid application ID and token WHEN invoke THEN token is sent successfully`() = runTest {
|
||||
// GIVEN
|
||||
val applicationId = "test-app-id"
|
||||
val applicationId = ApplicationId("test-app-id")
|
||||
val token = "test-token"
|
||||
coEvery { getApplicationIdUseCase() } returns Either.Right(applicationId)
|
||||
coEvery { pushNotificationsTokenProvider.getToken() } returns token
|
||||
coEvery { notificationsRepository.sendPushToken(applicationId, token) } returns Unit
|
||||
|
||||
// WHEN
|
||||
val result = sendPushTokenUseCase()
|
||||
val result = sendPushTokenUseCase(applicationId)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(Either.Right(Unit))
|
||||
coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN application ID is not found WHEN invoke THEN throws error`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { getApplicationIdUseCase() } returns Either.Left(Throwable("Application ID not found"))
|
||||
|
||||
// WHEN & THEN
|
||||
val result = sendPushTokenUseCase()
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
assertThat(result.fold({ it.message }, { null })).isEqualTo("Application ID not found")
|
||||
coVerify(exactly = 0) { notificationsRepository.sendPushToken(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN repository throws error WHEN invoke THEN returns error`() = runTest {
|
||||
// GIVEN
|
||||
val applicationId = "test-app-id"
|
||||
val applicationId = ApplicationId("test-app-id")
|
||||
val token = "test-token"
|
||||
val expectedError = RuntimeException("Network error")
|
||||
coEvery { getApplicationIdUseCase() } returns Either.Right(applicationId)
|
||||
coEvery { pushNotificationsTokenProvider.getToken() } returns token
|
||||
coEvery { notificationsRepository.sendPushToken(applicationId, token) } throws expectedError
|
||||
|
||||
// WHEN
|
||||
val result = sendPushTokenUseCase()
|
||||
val result = sendPushTokenUseCase(applicationId)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(Either.Left(expectedError))
|
||||
|
|
|
|||
1
domain/notifications/toggles/.gitignore
vendored
Normal file
1
domain/notifications/toggles/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
4
domain/notifications/toggles/build.gradle.kts
Normal file
4
domain/notifications/toggles/build.gradle.kts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
plugins {
|
||||
alias(deps.plugins.kotlin.jvm)
|
||||
id("configuration")
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.domain.notifications.toggles
|
||||
|
||||
interface NotificationsFeatureToggles {
|
||||
val isNotificationsEnabled: Boolean
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ plugins {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.domain.models)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.onramp.model
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.domain.onramp
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
import com.tangem.domain.onramp.repositories.OnrampTransactionRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
||||
class GetOnrampRedirectUrlUseCase(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue