Updated on 2026-08-14
This commit is contained in:
parent
a667fcc955
commit
db6157cae4
20 changed files with 435 additions and 500 deletions
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
|
@ -15,6 +16,8 @@ class CryptoCurrencyFactory(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
|
||||
private val networkFactory by lazy(LazyThreadSafetyMode.NONE) { NetworkFactory(excludedBlockchains) }
|
||||
|
||||
@Suppress("LongParameterList") // Yep, it's long
|
||||
fun createToken(
|
||||
network: Network,
|
||||
|
|
@ -49,7 +52,12 @@ class CryptoCurrencyFactory(
|
|||
return null
|
||||
}
|
||||
|
||||
val network = getNetwork(blockchain, extraDerivationPath, scanResponse, excludedBlockchains) ?: return null
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
||||
return CryptoCurrency.Token(
|
||||
|
|
@ -73,7 +81,12 @@ class CryptoCurrencyFactory(
|
|||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
return null
|
||||
}
|
||||
val network = getNetwork(blockchain, extraDerivationPath, scanResponse, excludedBlockchains) ?: return null
|
||||
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
|
||||
return createCoin(network)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,310 +1,8 @@
|
|||
package com.tangem.data.common.currency
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.FeePaidCurrency
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import timber.log.Timber
|
||||
|
||||
fun getBlockchain(networkId: Network.ID): Blockchain {
|
||||
return Blockchain.fromId(networkId.rawId.value)
|
||||
}
|
||||
|
||||
fun getNetwork(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
canHandleTokens: Boolean,
|
||||
): Network? {
|
||||
if (!isBlockchainSupported(blockchain, excludedBlockchains)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val derivationPath = getNetworkDerivationPath(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
cardDerivationStyleProvider = derivationStyleProvider,
|
||||
)
|
||||
|
||||
return Network(
|
||||
id = Network.ID(value = blockchain.id, derivationPath = derivationPath),
|
||||
backendId = blockchain.toNetworkId(),
|
||||
name = blockchain.fullName,
|
||||
isTestnet = blockchain.isTestnet(),
|
||||
derivationPath = derivationPath,
|
||||
currencySymbol = blockchain.currency,
|
||||
standardType = getNetworkStandardType(blockchain),
|
||||
hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource,
|
||||
canHandleTokens = canHandleTokens,
|
||||
transactionExtrasType = blockchain.getSupportedTransactionExtras(),
|
||||
)
|
||||
}
|
||||
|
||||
fun getNetwork(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
scanResponse: ScanResponse,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): Network? {
|
||||
val blockchain = getBlockchain(networkId)
|
||||
if (!isBlockchainSupported(blockchain, excludedBlockchains)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Network(
|
||||
id = networkId,
|
||||
backendId = blockchain.toNetworkId(),
|
||||
name = blockchain.fullName,
|
||||
isTestnet = blockchain.isTestnet(),
|
||||
derivationPath = derivationPath,
|
||||
currencySymbol = blockchain.currency,
|
||||
standardType = getNetworkStandardType(blockchain),
|
||||
hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource,
|
||||
canHandleTokens = scanResponse.card.canHandleToken(
|
||||
blockchain,
|
||||
scanResponse.cardTypesResolver,
|
||||
excludedBlockchains,
|
||||
),
|
||||
transactionExtrasType = blockchain.getSupportedTransactionExtras(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isBlockchainSupported(blockchain: Blockchain, excludedBlockchains: ExcludedBlockchains): Boolean {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
Timber.w("Unable to convert Unknown blockchain to the domain network model")
|
||||
return false
|
||||
}
|
||||
if (blockchain in excludedBlockchains) {
|
||||
Timber.w("Unable to convert excluded blockchain to the domain network model")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun getNetwork(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
scanResponse: ScanResponse,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): Network? {
|
||||
return getNetwork(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
canHandleTokens = scanResponse.card.canHandleToken(
|
||||
blockchain,
|
||||
scanResponse.cardTypesResolver,
|
||||
excludedBlockchains,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun getNetworkDerivationPath(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
cardDerivationStyleProvider: DerivationStyleProvider?,
|
||||
): Network.DerivationPath {
|
||||
if (cardDerivationStyleProvider == null) {
|
||||
return Network.DerivationPath.None
|
||||
}
|
||||
|
||||
val defaultDerivationPath = getDefaultDerivationPath(blockchain, cardDerivationStyleProvider)
|
||||
|
||||
return if (extraDerivationPath.isNullOrBlank()) {
|
||||
if (defaultDerivationPath.isNullOrBlank()) {
|
||||
Network.DerivationPath.None
|
||||
} else {
|
||||
Network.DerivationPath.Card(defaultDerivationPath)
|
||||
}
|
||||
} else {
|
||||
if (extraDerivationPath == defaultDerivationPath) {
|
||||
Network.DerivationPath.Card(defaultDerivationPath)
|
||||
} else {
|
||||
Network.DerivationPath.Custom(extraDerivationPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType {
|
||||
return when (blockchain) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.StandardType.ERC20
|
||||
Blockchain.BSC, Blockchain.BSCTestnet -> Network.StandardType.BEP20
|
||||
Blockchain.Binance, Blockchain.BinanceTestnet -> Network.StandardType.BEP2
|
||||
Blockchain.Tron, Blockchain.TronTestnet -> Network.StandardType.TRC20
|
||||
else -> Network.StandardType.Unspecified(blockchain.name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultDerivationPath(
|
||||
blockchain: Blockchain,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): String? {
|
||||
return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun Blockchain.getSupportedTransactionExtras(): Network.TransactionExtrasType {
|
||||
return when (this) {
|
||||
Blockchain.XRP -> Network.TransactionExtrasType.DESTINATION_TAG
|
||||
Blockchain.Binance,
|
||||
Blockchain.TON,
|
||||
Blockchain.Cosmos,
|
||||
Blockchain.TerraV1,
|
||||
Blockchain.TerraV2,
|
||||
Blockchain.Stellar,
|
||||
Blockchain.Hedera,
|
||||
Blockchain.Algorand,
|
||||
Blockchain.Sei,
|
||||
Blockchain.InternetComputer,
|
||||
Blockchain.Casper,
|
||||
-> Network.TransactionExtrasType.MEMO
|
||||
// region Other blockchains
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Alephium,
|
||||
Blockchain.AlephiumTestnet,
|
||||
Blockchain.Arbitrum,
|
||||
Blockchain.ArbitrumTestnet,
|
||||
Blockchain.Avalanche,
|
||||
Blockchain.AvalancheTestnet,
|
||||
Blockchain.BinanceTestnet,
|
||||
Blockchain.BSC,
|
||||
Blockchain.BSCTestnet,
|
||||
Blockchain.Bitcoin,
|
||||
Blockchain.BitcoinTestnet,
|
||||
Blockchain.BitcoinCash,
|
||||
Blockchain.BitcoinCashTestnet,
|
||||
Blockchain.Cardano,
|
||||
Blockchain.CosmosTestnet,
|
||||
Blockchain.Dogecoin,
|
||||
Blockchain.Ducatus,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.EthereumTestnet,
|
||||
Blockchain.EthereumClassic,
|
||||
Blockchain.EthereumClassicTestnet,
|
||||
Blockchain.Fantom,
|
||||
Blockchain.FantomTestnet,
|
||||
Blockchain.Litecoin,
|
||||
Blockchain.Near,
|
||||
Blockchain.NearTestnet,
|
||||
Blockchain.Polkadot,
|
||||
Blockchain.PolkadotTestnet,
|
||||
Blockchain.Kava,
|
||||
Blockchain.KavaTestnet,
|
||||
Blockchain.Kusama,
|
||||
Blockchain.Polygon,
|
||||
Blockchain.PolygonTestnet,
|
||||
Blockchain.RSK,
|
||||
Blockchain.SeiTestnet,
|
||||
Blockchain.StellarTestnet,
|
||||
Blockchain.Solana,
|
||||
Blockchain.SolanaTestnet,
|
||||
Blockchain.Tezos,
|
||||
Blockchain.Tron,
|
||||
Blockchain.TronTestnet,
|
||||
Blockchain.Gnosis,
|
||||
Blockchain.Dash,
|
||||
Blockchain.Optimism,
|
||||
Blockchain.OptimismTestnet,
|
||||
Blockchain.Dischain,
|
||||
Blockchain.EthereumPow,
|
||||
Blockchain.EthereumPowTestnet,
|
||||
Blockchain.Kaspa,
|
||||
Blockchain.KaspaTestnet,
|
||||
Blockchain.Telos,
|
||||
Blockchain.TelosTestnet,
|
||||
Blockchain.TONTestnet,
|
||||
Blockchain.Ravencoin,
|
||||
Blockchain.Clore,
|
||||
Blockchain.RavencoinTestnet,
|
||||
Blockchain.Cronos,
|
||||
Blockchain.AlephZero,
|
||||
Blockchain.AlephZeroTestnet,
|
||||
Blockchain.OctaSpace,
|
||||
Blockchain.OctaSpaceTestnet,
|
||||
Blockchain.Chia,
|
||||
Blockchain.ChiaTestnet,
|
||||
Blockchain.Decimal,
|
||||
Blockchain.DecimalTestnet,
|
||||
Blockchain.XDC,
|
||||
Blockchain.XDCTestnet,
|
||||
Blockchain.VeChain,
|
||||
Blockchain.VeChainTestnet,
|
||||
Blockchain.Aptos,
|
||||
Blockchain.AptosTestnet,
|
||||
Blockchain.Playa3ull,
|
||||
Blockchain.Shibarium,
|
||||
Blockchain.ShibariumTestnet,
|
||||
Blockchain.AlgorandTestnet,
|
||||
Blockchain.HederaTestnet,
|
||||
Blockchain.Aurora,
|
||||
Blockchain.AuroraTestnet,
|
||||
Blockchain.Areon,
|
||||
Blockchain.AreonTestnet,
|
||||
Blockchain.PulseChain,
|
||||
Blockchain.PulseChainTestnet,
|
||||
Blockchain.ZkSyncEra,
|
||||
Blockchain.ZkSyncEraTestnet,
|
||||
Blockchain.Nexa,
|
||||
Blockchain.NexaTestnet,
|
||||
Blockchain.Moonbeam,
|
||||
Blockchain.MoonbeamTestnet,
|
||||
Blockchain.Manta,
|
||||
Blockchain.MantaTestnet,
|
||||
Blockchain.PolygonZkEVM,
|
||||
Blockchain.PolygonZkEVMTestnet,
|
||||
Blockchain.Radiant,
|
||||
Blockchain.Fact0rn,
|
||||
Blockchain.Base,
|
||||
Blockchain.BaseTestnet,
|
||||
Blockchain.Moonriver,
|
||||
Blockchain.MoonriverTestnet,
|
||||
Blockchain.Mantle,
|
||||
Blockchain.MantleTestnet,
|
||||
Blockchain.Flare,
|
||||
Blockchain.FlareTestnet,
|
||||
Blockchain.Taraxa,
|
||||
Blockchain.TaraxaTestnet,
|
||||
Blockchain.Koinos,
|
||||
Blockchain.KoinosTestnet,
|
||||
Blockchain.Joystream,
|
||||
Blockchain.Bittensor,
|
||||
Blockchain.Filecoin,
|
||||
Blockchain.Blast,
|
||||
Blockchain.BlastTestnet,
|
||||
Blockchain.Cyber,
|
||||
Blockchain.CyberTestnet,
|
||||
Blockchain.Sui,
|
||||
Blockchain.SuiTestnet,
|
||||
Blockchain.EnergyWebChain,
|
||||
Blockchain.EnergyWebChainTestnet,
|
||||
Blockchain.EnergyWebX,
|
||||
Blockchain.EnergyWebXTestnet,
|
||||
Blockchain.CasperTestnet,
|
||||
Blockchain.Core,
|
||||
Blockchain.CoreTestnet,
|
||||
Blockchain.Xodex,
|
||||
Blockchain.Canxium,
|
||||
Blockchain.Chiliz,
|
||||
Blockchain.ChilizTestnet,
|
||||
Blockchain.VanarChain,
|
||||
Blockchain.VanarChainTestnet,
|
||||
Blockchain.OdysseyChain, Blockchain.OdysseyChainTestnet,
|
||||
Blockchain.Bitrock, Blockchain.BitrockTestnet,
|
||||
Blockchain.Sonic, Blockchain.SonicTestnet,
|
||||
Blockchain.ApeChain, Blockchain.ApeChainTestnet,
|
||||
Blockchain.Scroll, Blockchain.ScrollTestnet,
|
||||
Blockchain.ZkLinkNova, Blockchain.ZkLinkNovaTestnet,
|
||||
Blockchain.Pepecoin, Blockchain.PepecoinTestnet,
|
||||
-> Network.TransactionExtrasType.NONE
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Token
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -12,9 +13,9 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
class ResponseCryptoCurrenciesFactory(
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
class ResponseCryptoCurrenciesFactory(excludedBlockchains: ExcludedBlockchains) {
|
||||
|
||||
private val networkFactory by lazy(LazyThreadSafetyMode.NONE) { NetworkFactory(excludedBlockchains) }
|
||||
|
||||
fun createCurrency(currencyId: String, response: UserTokensResponse, scanResponse: ScanResponse): CryptoCurrency {
|
||||
return response.tokens
|
||||
|
|
@ -75,11 +76,10 @@ class ResponseCryptoCurrenciesFactory(
|
|||
responseToken: UserTokensResponse.Token,
|
||||
scanResponse: ScanResponse,
|
||||
): CryptoCurrency.Coin? {
|
||||
val network = getNetwork(
|
||||
blockchain,
|
||||
responseToken.derivationPath,
|
||||
scanResponse,
|
||||
excludedBlockchains,
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = responseToken.derivationPath,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
|
||||
return CryptoCurrency.Coin(
|
||||
|
|
@ -111,11 +111,10 @@ class ResponseCryptoCurrenciesFactory(
|
|||
responseDerivationPath: String?,
|
||||
scanResponse: ScanResponse,
|
||||
): CryptoCurrency.Token? {
|
||||
val network = getNetwork(
|
||||
blockchain,
|
||||
responseDerivationPath,
|
||||
scanResponse,
|
||||
excludedBlockchains,
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = responseDerivationPath,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import com.tangem.crypto.hdWallet.DerivationPath
|
|||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.managetokens.utils.TokenAddressesConverter
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
|
|
@ -38,6 +39,7 @@ internal class DefaultCustomTokensRepository(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val userTokensSaver: UserTokensSaver,
|
||||
private val networkFactory: NetworkFactory,
|
||||
) : CustomTokensRepository {
|
||||
|
||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
||||
|
|
@ -74,7 +76,7 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
|
||||
storedCurrencies.tokens.none { token ->
|
||||
Blockchain.fromId(networkId.rawId.value).toNetworkId() == token.networkId &&
|
||||
getBlockchain(networkId).toNetworkId() == token.networkId &&
|
||||
derivationPath.value == token.derivationPath &&
|
||||
contractAddress.equals(token.contractAddress, ignoreCase = true)
|
||||
}
|
||||
|
|
@ -91,7 +93,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while finding token"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while finding token"
|
||||
}
|
||||
|
|
@ -143,7 +149,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while creating coin"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while creating coin"
|
||||
}
|
||||
|
|
@ -176,7 +186,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while creating custom token"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while creating custom token"
|
||||
}
|
||||
|
|
@ -246,11 +260,10 @@ internal class DefaultCustomTokensRepository(
|
|||
)
|
||||
|
||||
if (canHandleBlockchain) {
|
||||
getNetwork(
|
||||
networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.data.common.api.safeApiCall
|
|||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.common.utils.retryOnError
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
||||
|
|
@ -49,9 +50,10 @@ internal class DefaultManageTokensRepository(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
networkFactory: NetworkFactory,
|
||||
) : ManageTokensRepository {
|
||||
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(excludedBlockchains)
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(networkFactory, excludedBlockchains)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
// region getTokenListBatchFlow
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.managetokens.di
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
||||
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
|
|
@ -35,6 +36,7 @@ internal object ManageTokensDataModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
networkFactory: NetworkFactory,
|
||||
): ManageTokensRepository {
|
||||
return DefaultManageTokensRepository(
|
||||
tangemTechApi = tangemTechApi,
|
||||
|
|
@ -44,6 +46,7 @@ internal object ManageTokensDataModule {
|
|||
testnetTokensStorage = testnetTokensStorage,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
networkFactory = networkFactory,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -58,6 +61,7 @@ internal object ManageTokensDataModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
userTokensSaver: UserTokensSaver,
|
||||
networkFactory: NetworkFactory,
|
||||
): CustomTokensRepository {
|
||||
return DefaultCustomTokensRepository(
|
||||
tangemTechApi = tangemTechApi,
|
||||
|
|
@ -67,6 +71,7 @@ internal object ManageTokensDataModule {
|
|||
excludedBlockchains = excludedBlockchains,
|
||||
dispatchers = dispatchers,
|
||||
userTokensSaver = userTokensSaver,
|
||||
networkFactory = networkFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.common.currency.getCoinId
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.currency.getTokenId
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import timber.log.Timber
|
||||
|
||||
internal class ManagedCryptoCurrencyFactory(
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
|
||||
|
|
@ -98,11 +99,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
return null
|
||||
}
|
||||
|
||||
val network = getNetwork(
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
scanResponse = scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
) ?: return null
|
||||
val contractAddress = token.contractAddress
|
||||
|
||||
|
|
@ -172,11 +172,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
?.takeUnless { it in excludedBlockchains }
|
||||
?: return null
|
||||
|
||||
val network = getNetwork(
|
||||
blockchain,
|
||||
extraDerivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
excludedBlockchains,
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
derivationStyleProvider = scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains)
|
||||
} ?: true,
|
||||
|
|
@ -217,11 +216,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
val blockchain = Blockchain.fromNetworkId(token.networkId)
|
||||
|
||||
if (blockchain != null && blockchain !in excludedBlockchains) {
|
||||
getNetwork(
|
||||
blockchain,
|
||||
token.derivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
excludedBlockchains,
|
||||
networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
derivationStyleProvider = scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains)
|
||||
} ?: true,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.common.utils.retryOnError
|
||||
import com.tangem.data.markets.analytics.MarketsDataAnalyticsEvent
|
||||
import com.tangem.data.markets.converters.*
|
||||
|
|
@ -40,10 +40,11 @@ internal class DefaultMarketsTokenRepository(
|
|||
private val userWalletsStore: UserWalletsStore,
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val tokenExchangesStore: RuntimeStateStore<List<TokenMarketExchangesResponse.Exchange>>,
|
||||
private val maxApyStore: RuntimeStateStore<BigDecimal?>,
|
||||
private val networkFactory: NetworkFactory,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : MarketsTokenRepository {
|
||||
|
||||
private val tokenMarketInfoConverter: TokenMarketInfoConverter = TokenMarketInfoConverter(excludedBlockchains)
|
||||
|
|
@ -252,11 +253,10 @@ internal class DefaultMarketsTokenRepository(
|
|||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} else {
|
||||
val currencyNetwork = getNetwork(
|
||||
val currencyNetwork = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
) ?: return null
|
||||
|
||||
cryptoCurrencyFactory.createToken(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.markets.di
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.markets.DefaultMarketsTokenRepository
|
||||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
|
|
@ -30,6 +31,7 @@ internal object MarketsDataModule {
|
|||
analyticsEventHandler: AnalyticsEventHandler,
|
||||
cacheRegistry: CacheRegistry,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
networkFactory: NetworkFactory,
|
||||
): MarketsTokenRepository {
|
||||
return DefaultMarketsTokenRepository(
|
||||
marketsApi = marketsApi,
|
||||
|
|
@ -41,6 +43,7 @@ internal object MarketsDataModule {
|
|||
tokenExchangesStore = RuntimeStateStore(defaultValue = emptyList()),
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
maxApyStore = RuntimeStateStore(defaultValue = null),
|
||||
networkFactory = networkFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.data.networks.multi
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
|
|
@ -19,14 +18,14 @@ import timber.log.Timber
|
|||
* @property params params
|
||||
* @property networksStatusesStore networks statuses store
|
||||
* @property userWalletsStore user wallets store
|
||||
* @property excludedBlockchains excluded blockchains
|
||||
* @property networkFactory network factory
|
||||
* @property dispatchers dispatchers
|
||||
*/
|
||||
internal class DefaultMultiNetworkStatusProducer @AssistedInject constructor(
|
||||
@Assisted val params: MultiNetworkStatusProducer.Params,
|
||||
private val networksStatusesStore: NetworksStatusesStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MultiNetworkStatusProducer {
|
||||
|
||||
|
|
@ -34,8 +33,8 @@ internal class DefaultMultiNetworkStatusProducer @AssistedInject constructor(
|
|||
get() = setOf()
|
||||
|
||||
override fun produce(): Flow<Set<NetworkStatus>> {
|
||||
return networksStatusesStore
|
||||
.get(userWalletId = params.userWalletId)
|
||||
return networksStatusesStore.get(userWalletId = params.userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.mapNotNull { statuses ->
|
||||
val userWallet = userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
|
||||
|
|
@ -45,11 +44,10 @@ internal class DefaultMultiNetworkStatusProducer @AssistedInject constructor(
|
|||
}
|
||||
|
||||
statuses.mapNotNullTo(hashSetOf()) { status ->
|
||||
val network = getNetwork(
|
||||
val network = networkFactory.create(
|
||||
networkId = status.id,
|
||||
derivationPath = status.id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
) ?: return@mapNotNullTo null
|
||||
|
||||
NetworkStatus(network = network, value = status.value)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.data.networks.multi
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.common.test.utils.getEmittedValues
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.networks.models.SimpleNetworkStatus
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.data.networks.toSimple
|
||||
|
|
@ -15,247 +15,409 @@ import com.tangem.domain.common.configs.GenericCardConfig
|
|||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusProducer
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class DefaultMultiNetworkStatusProducerTest {
|
||||
|
||||
private val params = MultiNetworkStatusProducer.Params(userWalletId = userWallet.walletId)
|
||||
|
||||
private val networksStatusesStore = mockk<NetworksStatusesStore>()
|
||||
private val userWalletsStore = mockk<UserWalletsStore>()
|
||||
private val excludedBlockchains = mockk<ExcludedBlockchains>()
|
||||
private val networkFactory = mockk<NetworkFactory>()
|
||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||
|
||||
private val producer = DefaultMultiNetworkStatusProducer(
|
||||
params = params,
|
||||
networksStatusesStore = networksStatusesStore,
|
||||
userWalletsStore = userWalletsStore,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
networkFactory = networkFactory,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
every { excludedBlockchains.contains(any()) } returns false
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(networksStatusesStore, userWalletsStore, networkFactory)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is mapped for user wallet id from params`() = runTest {
|
||||
fun `flow is mapped for user wallet id from params`() = runTest {
|
||||
// Assert
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createVerified(ethNetwork),
|
||||
MockNetworkStatusFactory.createVerified(cardanoNetwork),
|
||||
)
|
||||
|
||||
val networksStatusesFlow = flowOf(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
val simpleStatuses = statuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
val networksStatusesFlow = flowOf(simpleStatuses)
|
||||
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
|
||||
val actual = producer.produce()
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.first().network
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.last().network
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
// Act
|
||||
val actual = producer.produce().let(::getEmittedValues)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
// Assert
|
||||
val expected = statuses
|
||||
|
||||
Truth.assertThat(values.size).isEqualTo(1)
|
||||
Truth.assertThat(values.first()).isEqualTo(statuses)
|
||||
Truth.assertThat(actual.size).isEqualTo(1)
|
||||
Truth.assertThat(actual.first()).isEqualTo(expected)
|
||||
|
||||
verifyOrder {
|
||||
networksStatusesStore.get(params.userWalletId)
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is updated if statuses are updated`() = runTest {
|
||||
fun `flow will updated if statuses are updated`() = runTest {
|
||||
// Arrange
|
||||
val networksStatusesFlow = MutableSharedFlow<Set<SimpleNetworkStatus>>(replay = 2)
|
||||
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
|
||||
val actual = producer.produce()
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
|
||||
// first emit
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createUnreachable(ethNetwork),
|
||||
MockNetworkStatusFactory.createUnreachable(cardanoNetwork),
|
||||
)
|
||||
|
||||
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
val simpleStatuses = statuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
Truth.assertThat(values1.first()).isEqualTo(statuses)
|
||||
|
||||
// second emit
|
||||
val updatedStatuses = setOf(
|
||||
MockNetworkStatusFactory.createVerified(ethNetwork),
|
||||
MockNetworkStatusFactory.createVerified(cardanoNetwork),
|
||||
)
|
||||
|
||||
networksStatusesFlow.emit(updatedStatuses.map(NetworkStatus::toSimple).toSet())
|
||||
val updatedSimpleStatuses = updatedStatuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
// region every
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.first().network
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.last().network
|
||||
|
||||
val expected = listOf(statuses, updatedStatuses)
|
||||
Truth.assertThat(values2.size).isEqualTo(2)
|
||||
Truth.assertThat(values2).isEqualTo(expected)
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = updatedSimpleStatuses.first().id,
|
||||
derivationPath = updatedSimpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns updatedStatuses.first().network
|
||||
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = updatedSimpleStatuses.last().id,
|
||||
derivationPath = updatedSimpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns updatedStatuses.last().network
|
||||
// endregion
|
||||
|
||||
val producerFlow = producer.produce()
|
||||
|
||||
// Act 1 (first emit)
|
||||
networksStatusesFlow.emit(simpleStatuses)
|
||||
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
// Assert
|
||||
val expected1 = statuses
|
||||
|
||||
Truth.assertThat(actual1.size).isEqualTo(1)
|
||||
Truth.assertThat(actual1.first()).isEqualTo(expected1)
|
||||
|
||||
verifyOrder {
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
|
||||
// Act 2 (second emit)
|
||||
networksStatusesFlow.emit(updatedSimpleStatuses)
|
||||
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
// Assert
|
||||
val expected2 = listOf(statuses, updatedStatuses)
|
||||
|
||||
Truth.assertThat(actual2.size).isEqualTo(2)
|
||||
Truth.assertThat(actual2).isEqualTo(expected2)
|
||||
|
||||
verifyOrder {
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = updatedSimpleStatuses.first().id,
|
||||
derivationPath = updatedSimpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = updatedSimpleStatuses.last().id,
|
||||
derivationPath = updatedSimpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is filtered the same status`() = runTest {
|
||||
fun `flow is filtered the same status`() = runTest {
|
||||
// Arrange
|
||||
val networksStatusesFlow = MutableSharedFlow<Set<SimpleNetworkStatus>>(replay = 2)
|
||||
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createUnreachable(ethNetwork),
|
||||
MockNetworkStatusFactory.createUnreachable(cardanoNetwork),
|
||||
)
|
||||
|
||||
val simpleStatuses = statuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
// region every
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
|
||||
val actual = producer.produce()
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.first().network
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.last().network
|
||||
// endregion
|
||||
|
||||
// first emit
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createUnreachable(ethNetwork),
|
||||
MockNetworkStatusFactory.createUnreachable(cardanoNetwork),
|
||||
)
|
||||
val producerFlow = producer.produce()
|
||||
|
||||
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
// Act 1 (first emit)
|
||||
networksStatusesFlow.emit(simpleStatuses)
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
// Assert
|
||||
val expected1 = statuses
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
Truth.assertThat(values1.first()).isEqualTo(statuses)
|
||||
Truth.assertThat(actual1.size).isEqualTo(1)
|
||||
Truth.assertThat(actual1.first()).isEqualTo(expected1)
|
||||
|
||||
// second emit
|
||||
networksStatusesFlow.emit(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
verifyOrder {
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
// Act 2 (second emit)
|
||||
networksStatusesFlow.emit(simpleStatuses)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
Truth.assertThat(values2.size).isEqualTo(1)
|
||||
Truth.assertThat(values2.first()).isEqualTo(statuses)
|
||||
// Asset
|
||||
val expected2 = expected1
|
||||
Truth.assertThat(actual2.size).isEqualTo(1)
|
||||
Truth.assertThat(actual2.first()).isEqualTo(expected2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test if flow throws exception`() = runTest {
|
||||
fun `flow throws exception`() = runTest {
|
||||
// Arrange
|
||||
val exception = IllegalStateException()
|
||||
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createUnreachable(ethNetwork),
|
||||
MockNetworkStatusFactory.createUnreachable(cardanoNetwork),
|
||||
)
|
||||
|
||||
val simpleStatuses = statuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
val innerFlow = MutableStateFlow(value = false)
|
||||
val networksStatusesFlow = flow {
|
||||
if (innerFlow.value) {
|
||||
emit(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
emit(simpleStatuses)
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
.buffer(capacity = 5)
|
||||
|
||||
// region every
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.first().network
|
||||
every {
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} returns statuses.last().network
|
||||
// endregion
|
||||
|
||||
val actual = producer.produceWithFallback()
|
||||
val producerFlow = producer.produceWithFallback()
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
// Act 1 (fallback)
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
// Assert
|
||||
val expected1 = emptySet<NetworkStatus>()
|
||||
Truth.assertThat(actual1.size).isEqualTo(1)
|
||||
Truth.assertThat(actual1.first()).isEqualTo(expected1)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify(inverse = true) { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
Truth.assertThat(values1).isEqualTo(listOf(emptySet<NetworkStatus>()))
|
||||
verifyOrder(inverse = true) {
|
||||
userWalletsStore.getSyncOrNull(any())
|
||||
networkFactory.create(networkId = any(), derivationPath = any(), scanResponse = any())
|
||||
}
|
||||
|
||||
// Act 2 (emit)
|
||||
innerFlow.emit(value = true)
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
// Assert
|
||||
val expected2 = statuses
|
||||
Truth.assertThat(actual2.size).isEqualTo(1)
|
||||
Truth.assertThat(actual2.first()).isEqualTo(expected2)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
|
||||
Truth.assertThat(values2.size).isEqualTo(1)
|
||||
Truth.assertThat(values2).isEqualTo(listOf(statuses))
|
||||
verifyOrder {
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is empty`() = runTest {
|
||||
fun `flow is empty if store returns empty flow`() = runTest {
|
||||
// Arrange
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns emptyFlow()
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
|
||||
val actual = producer.produce()
|
||||
// Act
|
||||
val actual = producer.produce().let(::getEmittedValues)
|
||||
|
||||
// Assert
|
||||
val expected = emptySet<NetworkStatus>()
|
||||
Truth.assertThat(actual.size).isEqualTo(1)
|
||||
Truth.assertThat(actual.first()).isEqualTo(expected)
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
|
||||
verify(inverse = true) { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
|
||||
Truth.assertThat(values.size).isEqualTo(1)
|
||||
Truth.assertThat(values).isEqualTo(listOf(emptySet<NetworkStatus>()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test if network is null`() = runTest {
|
||||
fun `flow returns empty list if networkFactory returns null`() = runTest {
|
||||
// Arrange
|
||||
val statuses = setOf(
|
||||
MockNetworkStatusFactory.createVerified(ethNetwork),
|
||||
MockNetworkStatusFactory.createVerified(cardanoNetwork),
|
||||
)
|
||||
|
||||
val networksStatusesFlow = flowOf(statuses.map(NetworkStatus::toSimple).toSet())
|
||||
val simpleStatuses = statuses.map(NetworkStatus::toSimple).toSet()
|
||||
|
||||
val networksStatusesFlow = flowOf(simpleStatuses)
|
||||
|
||||
every { networksStatusesStore.get(params.userWalletId) } returns networksStatusesFlow
|
||||
every { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet
|
||||
every { excludedBlockchains.contains(any()) } returns true // cause network is null
|
||||
coEvery { networkFactory.create(networkId = any(), any(), any()) } returns null
|
||||
|
||||
val actual = producer.produce()
|
||||
// Act
|
||||
val actual = producer.produce().let(::getEmittedValues)
|
||||
|
||||
// check after producer.produce()
|
||||
verify { networksStatusesStore.get(params.userWalletId) }
|
||||
// Assert
|
||||
val expected = emptySet<NetworkStatus>()
|
||||
Truth.assertThat(actual.size).isEqualTo(1)
|
||||
Truth.assertThat(actual.first()).isEqualTo(expected)
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
|
||||
// Check after flow was observed by subscriber (getEmittedValues).
|
||||
// Otherwise, userWalletsStore.getSyncOrNull is not called.
|
||||
verify { userWalletsStore.getSyncOrNull(params.userWalletId) }
|
||||
|
||||
Truth.assertThat(values.size).isEqualTo(1)
|
||||
Truth.assertThat(values).isEqualTo(listOf(emptySet<NetworkStatus>()))
|
||||
verifyOrder {
|
||||
networksStatusesStore.get(params.userWalletId)
|
||||
userWalletsStore.getSyncOrNull(params.userWalletId)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.first().id,
|
||||
derivationPath = simpleStatuses.first().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
networkFactory.create(
|
||||
networkId = simpleStatuses.last().id,
|
||||
derivationPath = simpleStatuses.last().id.derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.local.nft.NFTPersistenceStore
|
||||
import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory
|
||||
import com.tangem.datasource.local.nft.NFTRuntimeStore
|
||||
|
|
@ -46,9 +46,10 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
private val nftRuntimeStoreFactory: NFTRuntimeStoreFactory,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val nftFeatureToggles: NFTFeatureToggles,
|
||||
private val networkFactory: NetworkFactory,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
resources: Resources,
|
||||
) : NFTRepository {
|
||||
|
||||
|
|
@ -200,11 +201,10 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
.entries
|
||||
.filter { it.canHandleNFTs() && !it.isTestnet() }
|
||||
.mapNotNull {
|
||||
getNetwork(
|
||||
networkFactory.create(
|
||||
blockchain = it,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.nft.di
|
|||
|
||||
import android.content.Context
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.nft.DefaultNFTRepository
|
||||
import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory
|
||||
import com.tangem.datasource.local.nft.NFTRuntimeStoreFactory
|
||||
|
|
@ -32,6 +33,7 @@ internal object NFTDataModule {
|
|||
excludedBlockchains: ExcludedBlockchains,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
nftFeatureToggles: NFTFeatureToggles,
|
||||
networkFactory: NetworkFactory,
|
||||
): NFTRepository = DefaultNFTRepository(
|
||||
nftPersistenceStoreFactory = nftPersistenceStoreFactory,
|
||||
nftRuntimeStoreFactory = nftRuntimeStoreFactory,
|
||||
|
|
@ -40,6 +42,7 @@ internal object NFTDataModule {
|
|||
excludedBlockchains = excludedBlockchains,
|
||||
userWalletsStore = userWalletsStore,
|
||||
nftFeatureToggles = nftFeatureToggles,
|
||||
networkFactory = networkFactory,
|
||||
resources = context.resources,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.HotCryptoResponse
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -20,17 +20,18 @@ import java.math.BigDecimal
|
|||
*
|
||||
* @property scanResponse scan response
|
||||
* @property imageHost image host
|
||||
* @property excludedBlockchains excluded blockchains
|
||||
* @param excludedBlockchains excluded blockchains
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class HotCryptoCurrencyConverter(
|
||||
private val scanResponse: ScanResponse,
|
||||
private val imageHost: String?,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : Converter<HotCryptoResponse.Token, HotCryptoCurrency?> {
|
||||
|
||||
private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory(excludedBlockchains) }
|
||||
private val cryptoCurrencyFactory by lazy(LazyThreadSafetyMode.NONE) { CryptoCurrencyFactory(excludedBlockchains) }
|
||||
private val networkFactory by lazy(LazyThreadSafetyMode.NONE) { NetworkFactory(excludedBlockchains) }
|
||||
|
||||
override fun convert(value: HotCryptoResponse.Token): HotCryptoCurrency? {
|
||||
val rawId = value.id?.let(CryptoCurrency::RawID) ?: return null
|
||||
|
|
@ -77,11 +78,10 @@ internal class HotCryptoCurrencyConverter(
|
|||
private fun createNetwork(networkId: String): Network? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId) ?: return null
|
||||
|
||||
return getNetwork(
|
||||
return networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package com.tangem.data.visa.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
|
|
@ -19,7 +18,7 @@ import javax.inject.Inject
|
|||
|
||||
internal class VisaCurrencyFactory @Inject constructor(
|
||||
private val cryptoCurrencyFactory: CryptoCurrencyFactory,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val networkFactory: NetworkFactory,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, contractInfo: VisaContractInfo, fiatRate: BigDecimal): VisaCurrency {
|
||||
|
|
@ -31,11 +30,10 @@ internal class VisaCurrencyFactory @Inject constructor(
|
|||
}
|
||||
val remainingOtpLimit = getRemainingOtp(currentLimit, now)
|
||||
|
||||
val currencyNetwork = getNetwork(
|
||||
val currencyNetwork = networkFactory.create(
|
||||
blockchain = Blockchain.Polygon,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
canHandleTokens = true,
|
||||
) ?: error("Unable to create network for Visa currency")
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.data.walletconnect.utils
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.walletconnect.model.CAIP2
|
||||
import com.tangem.data.walletconnect.model.NamespaceKey
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -21,11 +21,11 @@ internal interface WcNamespaceConverter {
|
|||
fun toNetwork(chainId: String, wallet: UserWallet): Network?
|
||||
fun toNetwork(chainId: String, wallet: UserWallet, excludedBlockchains: ExcludedBlockchains): Network? {
|
||||
val blockchain = toBlockchain(chainId) ?: return null
|
||||
return getNetwork(
|
||||
|
||||
return NetworkFactory(excludedBlockchains).create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = wallet.scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue