Updated on 2026-08-14
This commit is contained in:
parent
9cda183a5b
commit
3171058cb6
16 changed files with 796 additions and 222 deletions
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.tangem.datasource.di.local
|
||||||
|
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
|
import com.tangem.datasource.local.token.DefaultUserTokensResponseStore
|
||||||
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object LocalTokenModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideUserTokensResponseStore(appPreferencesStore: AppPreferencesStore): UserTokensResponseStore {
|
||||||
|
return DefaultUserTokensResponseStore(appPreferencesStore = appPreferencesStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.tangem.datasource.local.token
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
|
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
|
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of [UserTokensResponseStore]
|
||||||
|
*
|
||||||
|
* @property appPreferencesStore app preferences store
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
internal class DefaultUserTokensResponseStore(
|
||||||
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
|
) : UserTokensResponseStore {
|
||||||
|
|
||||||
|
override suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse? {
|
||||||
|
return appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||||
|
key = PreferencesKeys.getUserTokensKey(userWalletId = userWalletId.stringValue),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.tangem.datasource.local.token
|
||||||
|
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store of [UserTokensResponse]
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
interface UserTokensResponseStore {
|
||||||
|
|
||||||
|
/** Get [UserTokensResponse] synchronously by [userWalletId] or null */
|
||||||
|
suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse?
|
||||||
|
}
|
||||||
|
|
@ -14,9 +14,11 @@ dependencies {
|
||||||
implementation(projects.core.datasource)
|
implementation(projects.core.datasource)
|
||||||
|
|
||||||
/* Domain */
|
/* Domain */
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.demo)
|
||||||
implementation(projects.domain.legacy)
|
implementation(projects.domain.legacy)
|
||||||
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.tokens.models)
|
implementation(projects.domain.tokens.models)
|
||||||
|
implementation(projects.domain.wallets.models)
|
||||||
|
|
||||||
/* Libs - SDK */
|
/* Libs - SDK */
|
||||||
implementation(tangemDeps.blockchain)
|
implementation(tangemDeps.blockchain)
|
||||||
|
|
@ -28,8 +30,16 @@ dependencies {
|
||||||
kapt(deps.hilt.kapt)
|
kapt(deps.hilt.kapt)
|
||||||
|
|
||||||
/* Libs - Other */
|
/* Libs - Other */
|
||||||
implementation(deps.kotlin.coroutines)
|
implementation(deps.androidx.datastore)
|
||||||
implementation(deps.jodatime)
|
|
||||||
implementation(deps.timber)
|
|
||||||
implementation(deps.arrow.core)
|
implementation(deps.arrow.core)
|
||||||
|
implementation(deps.jodatime)
|
||||||
|
implementation(deps.kotlin.coroutines)
|
||||||
|
implementation(deps.timber)
|
||||||
|
|
||||||
|
/* Test */
|
||||||
|
testImplementation(projects.common.test)
|
||||||
|
testImplementation(deps.test.coroutine)
|
||||||
|
testImplementation(deps.test.junit)
|
||||||
|
testImplementation(deps.test.mockk)
|
||||||
|
testImplementation(deps.test.truth)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.tangem.data.common.currency
|
||||||
|
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating list of [CryptoCurrency] for selected card
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
interface CardCryptoCurrencyFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal method for creating list of [CryptoCurrency] in [network] for any card
|
||||||
|
*
|
||||||
|
* @param userWalletId user wallet id that determines type of card
|
||||||
|
* @param network network
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
suspend fun create(userWalletId: UserWalletId, network: Network): List<CryptoCurrency>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create default coins for multi currency card
|
||||||
|
*
|
||||||
|
* @param scanResponse scan response
|
||||||
|
*/
|
||||||
|
fun createDefaultCoinsForMultiCurrencyCard(scanResponse: ScanResponse): List<CryptoCurrency.Coin>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create primary currency for single currency card
|
||||||
|
*
|
||||||
|
* @param scanResponse scan response
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create currencies for single currency card with token (like, NODL)
|
||||||
|
*
|
||||||
|
* @param scanResponse scan response
|
||||||
|
*/
|
||||||
|
@Throws
|
||||||
|
fun createCurrenciesForSingleCurrencyCardWithToken(scanResponse: ScanResponse): List<CryptoCurrency>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
package com.tangem.data.common.currency
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||||
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
|
import com.tangem.domain.demo.DemoConfig
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of factory for creating list of [CryptoCurrency] for selected card
|
||||||
|
*
|
||||||
|
* @property demoConfig demo config
|
||||||
|
* @property excludedBlockchains excluded blockchains
|
||||||
|
* @property userWalletsStore user wallets store
|
||||||
|
* @property userTokensResponseStore user tokens response store
|
||||||
|
*/
|
||||||
|
internal class DefaultCardCryptoCurrencyFactory(
|
||||||
|
private val demoConfig: DemoConfig,
|
||||||
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
|
private val userWalletsStore: UserWalletsStore,
|
||||||
|
private val userTokensResponseStore: UserTokensResponseStore,
|
||||||
|
) : CardCryptoCurrencyFactory {
|
||||||
|
|
||||||
|
private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory(excludedBlockchains) }
|
||||||
|
|
||||||
|
override suspend fun create(userWalletId: UserWalletId, network: Network): List<CryptoCurrency> {
|
||||||
|
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
|
||||||
|
val blockchain = Blockchain.fromNetworkId(networkId = network.backendId)
|
||||||
|
|
||||||
|
// multi-currency wallet
|
||||||
|
if (userWallet.isMultiCurrency) return getMultiWalletCurrencies(userWallet = userWallet, network = network)
|
||||||
|
|
||||||
|
// check if the blockchain of single-currency wallet is the same as network
|
||||||
|
val cardBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
|
||||||
|
if (cardBlockchain != blockchain) return emptyList()
|
||||||
|
|
||||||
|
// single-currency wallet with token (NODL)
|
||||||
|
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
||||||
|
return createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// single-currency wallet
|
||||||
|
return createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse).let(::listOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createDefaultCoinsForMultiCurrencyCard(scanResponse: ScanResponse): List<CryptoCurrency.Coin> {
|
||||||
|
val card = scanResponse.card
|
||||||
|
|
||||||
|
var blockchains = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||||
|
demoConfig.demoBlockchains
|
||||||
|
} else {
|
||||||
|
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.isTestCard) {
|
||||||
|
blockchains = blockchains.mapNotNull { it.getTestnetVersion() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockchains.mapNotNull {
|
||||||
|
cryptoCurrencyFactory.createCoin(
|
||||||
|
blockchain = it,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = scanResponse,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency {
|
||||||
|
return with(getSingleWalletCurrencies(scanResponse)) {
|
||||||
|
primaryToken ?: coin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCurrenciesForSingleCurrencyCardWithToken(scanResponse: ScanResponse): List<CryptoCurrency> {
|
||||||
|
return with(getSingleWalletCurrencies(scanResponse)) {
|
||||||
|
listOfNotNull(coin, primaryToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getMultiWalletCurrencies(userWallet: UserWallet, network: Network): List<CryptoCurrency> {
|
||||||
|
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId)
|
||||||
|
?: return emptyList()
|
||||||
|
|
||||||
|
val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||||
|
|
||||||
|
return responseCurrenciesFactory.createCurrencies(
|
||||||
|
tokens = response.tokens.filter {
|
||||||
|
it.networkId == network.backendId && it.derivationPath == network.derivationPath.value
|
||||||
|
},
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSingleWalletCurrencies(scanResponse: ScanResponse): SingleWalletCurrencies {
|
||||||
|
val resolver = scanResponse.cardTypesResolver
|
||||||
|
val blockchain = resolver.getBlockchain()
|
||||||
|
|
||||||
|
val coin = cryptoCurrencyFactory.createCoin(
|
||||||
|
blockchain = blockchain,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
requireNotNull(coin) { "Coin for the single currency card cannot be null" }
|
||||||
|
|
||||||
|
val primaryToken = resolver.getPrimaryToken()?.let { token ->
|
||||||
|
cryptoCurrencyFactory.createToken(
|
||||||
|
sdkToken = token,
|
||||||
|
blockchain = blockchain,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = scanResponse,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SingleWalletCurrencies(coin = coin, primaryToken = primaryToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class SingleWalletCurrencies(val coin: CryptoCurrency, val primaryToken: CryptoCurrency?)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.tangem.data.common.di
|
||||||
|
|
||||||
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
|
import com.tangem.data.common.currency.DefaultCardCryptoCurrencyFactory
|
||||||
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.demo.DemoConfig
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object DataCommonModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideCardCryptoCurrencyFactory(
|
||||||
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
userWalletsStore: UserWalletsStore,
|
||||||
|
userTokensResponseStore: UserTokensResponseStore,
|
||||||
|
): CardCryptoCurrencyFactory {
|
||||||
|
return DefaultCardCryptoCurrencyFactory(
|
||||||
|
demoConfig = DemoConfig(),
|
||||||
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
userWalletsStore = userWalletsStore,
|
||||||
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,430 @@
|
||||||
|
package com.tangem.data.common.currency
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.google.common.truth.Truth
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.common.card.WalletData
|
||||||
|
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||||
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
|
import com.tangem.datasource.local.token.UserTokensResponseStore
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.common.configs.GenericCardConfig
|
||||||
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
|
import com.tangem.domain.demo.DemoConfig
|
||||||
|
import com.tangem.domain.models.scan.ProductType
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
import io.mockk.*
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
internal class DefaultCardCryptoCurrencyFactoryTest {
|
||||||
|
|
||||||
|
private val userWalletsStore: UserWalletsStore = mockk()
|
||||||
|
private val userTokensResponseStore: UserTokensResponseStore = mockk()
|
||||||
|
|
||||||
|
private val factory = DefaultCardCryptoCurrencyFactory(
|
||||||
|
demoConfig = DemoConfig(),
|
||||||
|
excludedBlockchains = ExcludedBlockchains(),
|
||||||
|
userWalletsStore = userWalletsStore,
|
||||||
|
userTokensResponseStore = userTokensResponseStore,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
mockkStatic(Uri::class)
|
||||||
|
every { Uri.parse(any()) } returns mockk()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if userTokensResponse is not empty`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet()
|
||||||
|
|
||||||
|
val userTokensResponse = UserTokensResponseFactory().createUserTokensResponse(
|
||||||
|
currencies = listOf(ethereum),
|
||||||
|
isGroupedByNetwork = false,
|
||||||
|
isSortedByBalance = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = multiWallet.walletId) } returns multiWallet
|
||||||
|
coEvery { userTokensResponseStore.getSyncOrNull(multiWallet.walletId) } returns userTokensResponse
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = multiWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = multiWallet.walletId)
|
||||||
|
userTokensResponseStore.getSyncOrNull(multiWallet.walletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = listOf(ethereum)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if userTokensResponse is empty`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet()
|
||||||
|
|
||||||
|
val userTokensResponse = UserTokensResponseFactory().createUserTokensResponse(
|
||||||
|
currencies = listOf(),
|
||||||
|
isGroupedByNetwork = false,
|
||||||
|
isSortedByBalance = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = multiWallet.walletId) } returns multiWallet
|
||||||
|
coEvery { userTokensResponseStore.getSyncOrNull(multiWallet.walletId) } returns userTokensResponse
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = multiWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = multiWallet.walletId)
|
||||||
|
userTokensResponseStore.getSyncOrNull(multiWallet.walletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = emptyList<CryptoCurrency>()
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if userTokensResponse is null`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet()
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = multiWallet.walletId) } returns multiWallet
|
||||||
|
coEvery { userTokensResponseStore.getSyncOrNull(multiWallet.walletId) } returns null
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = multiWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = multiWallet.walletId)
|
||||||
|
userTokensResponseStore.getSyncOrNull(multiWallet.walletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = emptyList<CryptoCurrency>()
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if userTokensResponse does not contain currency of selected network`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet()
|
||||||
|
|
||||||
|
val userTokensResponse = UserTokensResponseFactory().createUserTokensResponse(
|
||||||
|
currencies = listOf(bitcoin),
|
||||||
|
isGroupedByNetwork = false,
|
||||||
|
isSortedByBalance = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = multiWallet.walletId) } returns multiWallet
|
||||||
|
coEvery { userTokensResponseStore.getSyncOrNull(multiWallet.walletId) } returns userTokensResponse
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = multiWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = multiWallet.walletId)
|
||||||
|
userTokensResponseStore.getSyncOrNull(multiWallet.walletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = emptyList<CryptoCurrency>()
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if single wallet has another primary network`() = runTest {
|
||||||
|
val singleWallet = createSingleWallet()
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = singleWallet.walletId) } returns singleWallet
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = singleWallet.walletId, network = bitcoin.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = singleWallet.walletId)
|
||||||
|
singleWallet.scanResponse.cardTypesResolver.getBlockchain()
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = emptyList<CryptoCurrency>()
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if card is single wallet`() = runTest {
|
||||||
|
val singleWallet = createSingleWallet()
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = singleWallet.walletId) } returns singleWallet
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = singleWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
userWalletsStore.getSyncStrict(key = singleWallet.walletId)
|
||||||
|
singleWallet.scanResponse.cardTypesResolver.getBlockchain()
|
||||||
|
}
|
||||||
|
|
||||||
|
val expected = listOf(ethereum)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test create if card is single wallet with token`() = runTest {
|
||||||
|
val singleWallet = createSingleWalletWithToken()
|
||||||
|
|
||||||
|
coEvery { userWalletsStore.getSyncStrict(key = singleWallet.walletId) } returns singleWallet
|
||||||
|
|
||||||
|
val actual = factory.create(userWalletId = singleWallet.walletId, network = ethereum.network)
|
||||||
|
|
||||||
|
val token = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains()).createToken(
|
||||||
|
sdkToken = singleWallet.scanResponse.cardTypesResolver.getPrimaryToken()!!,
|
||||||
|
blockchain = Blockchain.Ethereum,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = singleWallet.scanResponse,
|
||||||
|
)
|
||||||
|
val expected = listOf(ethereum, token)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createDefaultCoinsForMultiCurrencyCard if card is prod`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet()
|
||||||
|
|
||||||
|
val actual = factory.createDefaultCoinsForMultiCurrencyCard(scanResponse = multiWallet.scanResponse)
|
||||||
|
|
||||||
|
val expected = listOf(bitcoin, ethereum)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createDefaultCoinsForMultiCurrencyCard if card is test`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet().let {
|
||||||
|
it.copy(
|
||||||
|
scanResponse = it.scanResponse.copy(
|
||||||
|
card = it.scanResponse.card.copy(cardId = "FF99", batchId = "99FF"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val actual = factory.createDefaultCoinsForMultiCurrencyCard(scanResponse = multiWallet.scanResponse)
|
||||||
|
|
||||||
|
val expected = listOf(
|
||||||
|
cryptoCurrencyFactory.createCoin(blockchain = Blockchain.BitcoinTestnet),
|
||||||
|
cryptoCurrencyFactory.createCoin(blockchain = Blockchain.EthereumTestnet).setCanHandleTokens(true),
|
||||||
|
)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createDefaultCoinsForMultiCurrencyCard if card is demo`() = runTest {
|
||||||
|
val multiWallet = createMultiWallet().let {
|
||||||
|
it.copy(
|
||||||
|
scanResponse = it.scanResponse.copy(
|
||||||
|
card = it.scanResponse.card.copy(cardId = "AC01000000041225"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val actual = factory.createDefaultCoinsForMultiCurrencyCard(scanResponse = multiWallet.scanResponse)
|
||||||
|
|
||||||
|
val expected = listOf(
|
||||||
|
bitcoin,
|
||||||
|
ethereum,
|
||||||
|
cryptoCurrencyFactory.createCoin(blockchain = Blockchain.Dogecoin),
|
||||||
|
cryptoCurrencyFactory.createCoin(blockchain = Blockchain.Solana),
|
||||||
|
)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createPrimaryCurrencyForSingleCurrencyCard if unable to create token`() = runTest {
|
||||||
|
val singleWallet = UserWallet(
|
||||||
|
name = "Note",
|
||||||
|
walletId = UserWalletId("011"),
|
||||||
|
cardsInWallet = setOf(),
|
||||||
|
isMultiCurrency = false,
|
||||||
|
scanResponse = MockScanResponseFactory.create(
|
||||||
|
cardConfig = GenericCardConfig(maxWalletCount = 2),
|
||||||
|
derivedKeys = emptyMap(),
|
||||||
|
),
|
||||||
|
hasBackupError = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = runCatching {
|
||||||
|
factory.createPrimaryCurrencyForSingleCurrencyCard(scanResponse = singleWallet.scanResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
val exception = IllegalArgumentException("Coin for the single currency card cannot be null")
|
||||||
|
|
||||||
|
Truth.assertThat(actual.isFailure).isTrue()
|
||||||
|
Truth.assertThat(actual.exceptionOrNull()).isInstanceOf(exception::class.java)
|
||||||
|
Truth.assertThat(actual.exceptionOrNull()).hasMessageThat().isEqualTo(exception.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createPrimaryCurrencyForSingleCurrencyCard if primaryToken is null`() = runTest {
|
||||||
|
val singleWallet = createSingleWallet()
|
||||||
|
|
||||||
|
val actual = factory.createPrimaryCurrencyForSingleCurrencyCard(scanResponse = singleWallet.scanResponse)
|
||||||
|
|
||||||
|
val expected = ethereum
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createPrimaryCurrencyForSingleCurrencyCard if primaryToken is not null`() = runTest {
|
||||||
|
val singleWallet = createSingleWalletWithToken()
|
||||||
|
|
||||||
|
val actual = factory.createPrimaryCurrencyForSingleCurrencyCard(scanResponse = singleWallet.scanResponse)
|
||||||
|
|
||||||
|
val expected = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains()).createToken(
|
||||||
|
sdkToken = singleWallet.scanResponse.cardTypesResolver.getPrimaryToken()!!,
|
||||||
|
blockchain = Blockchain.Ethereum,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = singleWallet.scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createCurrenciesForSingleCurrencyCardWithToken if unable to create token`() = runTest {
|
||||||
|
val singleWalletWithToken = UserWallet(
|
||||||
|
name = "Note",
|
||||||
|
walletId = UserWalletId("011"),
|
||||||
|
cardsInWallet = setOf(),
|
||||||
|
isMultiCurrency = false,
|
||||||
|
scanResponse = MockScanResponseFactory.create(
|
||||||
|
cardConfig = GenericCardConfig(maxWalletCount = 2),
|
||||||
|
derivedKeys = emptyMap(),
|
||||||
|
),
|
||||||
|
hasBackupError = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
val actual = runCatching {
|
||||||
|
factory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = singleWalletWithToken.scanResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
val exception = IllegalArgumentException("Coin for the single currency card cannot be null")
|
||||||
|
|
||||||
|
Truth.assertThat(actual.isFailure).isTrue()
|
||||||
|
Truth.assertThat(actual.exceptionOrNull()).isInstanceOf(exception::class.java)
|
||||||
|
Truth.assertThat(actual.exceptionOrNull()).hasMessageThat().isEqualTo(exception.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createCurrenciesForSingleCurrencyCardWithToken if primaryToken is null`() = runTest {
|
||||||
|
val singleWalletWithToken = createSingleWallet()
|
||||||
|
|
||||||
|
val actual = factory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
|
scanResponse = singleWalletWithToken.scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = listOf(ethereum)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test createCurrenciesForSingleCurrencyCardWithToken if primaryToken is not null`() = runTest {
|
||||||
|
val singleWalletWithToken = createSingleWalletWithToken()
|
||||||
|
|
||||||
|
val actual = factory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
|
scanResponse = singleWalletWithToken.scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
val token = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains()).createToken(
|
||||||
|
sdkToken = singleWalletWithToken.scanResponse.cardTypesResolver.getPrimaryToken()!!,
|
||||||
|
blockchain = Blockchain.Ethereum,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = singleWalletWithToken.scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = listOf(ethereum, token)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMultiWallet(): UserWallet {
|
||||||
|
return UserWallet(
|
||||||
|
name = "Wallet 1",
|
||||||
|
walletId = UserWalletId("011"),
|
||||||
|
cardsInWallet = setOf(),
|
||||||
|
isMultiCurrency = true,
|
||||||
|
scanResponse = MockScanResponseFactory.create(
|
||||||
|
cardConfig = GenericCardConfig(maxWalletCount = 2),
|
||||||
|
derivedKeys = emptyMap(),
|
||||||
|
),
|
||||||
|
hasBackupError = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSingleWallet(): UserWallet {
|
||||||
|
return UserWallet(
|
||||||
|
name = "Note",
|
||||||
|
walletId = UserWalletId("011"),
|
||||||
|
cardsInWallet = setOf(),
|
||||||
|
isMultiCurrency = false,
|
||||||
|
scanResponse = MockScanResponseFactory.create(
|
||||||
|
cardConfig = GenericCardConfig(maxWalletCount = 2),
|
||||||
|
derivedKeys = emptyMap(),
|
||||||
|
).let {
|
||||||
|
it.copy(
|
||||||
|
card = it.card.copy(batchId = "AB10"),
|
||||||
|
productType = ProductType.Note,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
hasBackupError = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSingleWalletWithToken(): UserWallet {
|
||||||
|
return UserWallet(
|
||||||
|
name = "NODL",
|
||||||
|
walletId = UserWalletId("011"),
|
||||||
|
cardsInWallet = setOf(),
|
||||||
|
isMultiCurrency = false,
|
||||||
|
scanResponse = MockScanResponseFactory.create(
|
||||||
|
cardConfig = GenericCardConfig(maxWalletCount = 2),
|
||||||
|
derivedKeys = emptyMap(),
|
||||||
|
).copy(
|
||||||
|
productType = ProductType.Note,
|
||||||
|
walletData = WalletData(
|
||||||
|
blockchain = "ETH",
|
||||||
|
token = WalletData.Token(
|
||||||
|
name = "Ethereum",
|
||||||
|
symbol = "ETH",
|
||||||
|
contractAddress = "0x",
|
||||||
|
decimals = 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
hasBackupError = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
|
||||||
|
val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||||
|
|
||||||
|
val ethereum = cryptoCurrencyFactory.ethereum.setCanHandleTokens(value = true)
|
||||||
|
|
||||||
|
val bitcoin = cryptoCurrencyFactory.createCoin(blockchain = Blockchain.Bitcoin)
|
||||||
|
|
||||||
|
fun CryptoCurrency.setCanHandleTokens(value: Boolean): CryptoCurrency {
|
||||||
|
return when (this) {
|
||||||
|
is CryptoCurrency.Coin -> copy(network = network.copy(canHandleTokens = value))
|
||||||
|
is CryptoCurrency.Token -> copy(network = network.copy(canHandleTokens = value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,12 +6,12 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||||
import com.tangem.data.common.api.safeApiCall
|
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.UserTokensResponseFactory
|
||||||
import com.tangem.data.common.currency.getBlockchain
|
import com.tangem.data.common.currency.getBlockchain
|
||||||
import com.tangem.data.common.utils.retryOnError
|
import com.tangem.data.common.utils.retryOnError
|
||||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||||
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
||||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
|
|
@ -26,7 +26,6 @@ import com.tangem.domain.common.extensions.canHandleToken
|
||||||
import com.tangem.domain.common.extensions.supportedBlockchains
|
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||||
import com.tangem.domain.common.extensions.supportedTokens
|
import com.tangem.domain.common.extensions.supportedTokens
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.demo.DemoConfig
|
|
||||||
import com.tangem.domain.managetokens.model.*
|
import com.tangem.domain.managetokens.model.*
|
||||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||||
|
|
@ -48,12 +47,12 @@ internal class DefaultManageTokensRepository(
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val testnetTokensStorage: TestnetTokensStorage,
|
private val testnetTokensStorage: TestnetTokensStorage,
|
||||||
private val excludedBlockchains: ExcludedBlockchains,
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : ManageTokensRepository {
|
) : ManageTokensRepository {
|
||||||
|
|
||||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(excludedBlockchains)
|
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(excludedBlockchains)
|
||||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(DemoConfig(), excludedBlockchains)
|
|
||||||
|
|
||||||
// region getTokenListBatchFlow
|
// region getTokenListBatchFlow
|
||||||
override fun getTokenListBatchFlow(
|
override fun getTokenListBatchFlow(
|
||||||
|
|
@ -77,7 +76,7 @@ internal class DefaultManageTokensRepository(
|
||||||
prefetchDistance = batchSize,
|
prefetchDistance = batchSize,
|
||||||
batchSize = batchSize,
|
batchSize = batchSize,
|
||||||
subFetcher = { request, _, isFirstBatchFetching ->
|
subFetcher = { request, _, isFirstBatchFetching ->
|
||||||
val userWallet = request.params.userWalletId?.let { getUserWallet(it) }
|
val userWallet = request.params.userWalletId?.let(userWalletsStore::getSyncStrict)
|
||||||
|
|
||||||
if (userWallet?.scanResponse?.card?.isTestCard == true) {
|
if (userWallet?.scanResponse?.card?.isTestCard == true) {
|
||||||
fetchTestnetCurrencies(userWallet, request)
|
fetchTestnetCurrencies(userWallet, request)
|
||||||
|
|
@ -190,17 +189,11 @@ internal class DefaultManageTokensRepository(
|
||||||
|
|
||||||
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
|
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createUserTokensResponse(
|
||||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
||||||
isGroupedByNetwork = false,
|
isGroupedByNetwork = false,
|
||||||
isSortedByBalance = false,
|
isSortedByBalance = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
|
||||||
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
|
||||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSupportedBlockchains(userWallet: UserWallet?): List<Blockchain> {
|
private fun getSupportedBlockchains(userWallet: UserWallet?): List<Blockchain> {
|
||||||
return userWallet?.scanResponse?.let {
|
return userWallet?.scanResponse?.let {
|
||||||
it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains)
|
it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains)
|
||||||
|
|
@ -261,7 +254,7 @@ internal class DefaultManageTokensRepository(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
sourceNetwork: SourceNetwork,
|
sourceNetwork: SourceNetwork,
|
||||||
): CurrencyUnsupportedState? {
|
): CurrencyUnsupportedState? {
|
||||||
val userWallet = getUserWallet(userWalletId = userWalletId)
|
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
val blockchain = getBlockchain(sourceNetwork.id)
|
val blockchain = getBlockchain(sourceNetwork.id)
|
||||||
return when (sourceNetwork) {
|
return when (sourceNetwork) {
|
||||||
is SourceNetwork.Default -> checkTokenUnsupportedState(userWallet = userWallet, blockchain = blockchain)
|
is SourceNetwork.Default -> checkTokenUnsupportedState(userWallet = userWallet, blockchain = blockchain)
|
||||||
|
|
@ -274,7 +267,7 @@ internal class DefaultManageTokensRepository(
|
||||||
rawNetworkId: String,
|
rawNetworkId: String,
|
||||||
isMainNetwork: Boolean,
|
isMainNetwork: Boolean,
|
||||||
): CurrencyUnsupportedState? {
|
): CurrencyUnsupportedState? {
|
||||||
val userWallet = getUserWallet(userWalletId = userWalletId)
|
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
val blockchain = Blockchain.fromNetworkId(networkId = rawNetworkId)
|
val blockchain = Blockchain.fromNetworkId(networkId = rawNetworkId)
|
||||||
?: error("Can not create blockchain with given networkId -> $rawNetworkId")
|
?: error("Can not create blockchain with given networkId -> $rawNetworkId")
|
||||||
return if (isMainNetwork) {
|
return if (isMainNetwork) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.data.managetokens.di
|
package com.tangem.data.managetokens.di
|
||||||
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
||||||
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
||||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||||
|
|
@ -32,15 +33,17 @@ internal object ManageTokensDataModule {
|
||||||
testnetTokensStorage: TestnetTokensStorage,
|
testnetTokensStorage: TestnetTokensStorage,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
): ManageTokensRepository {
|
): ManageTokensRepository {
|
||||||
return DefaultManageTokensRepository(
|
return DefaultManageTokensRepository(
|
||||||
tangemTechApi,
|
tangemTechApi = tangemTechApi,
|
||||||
userWalletsStore,
|
userWalletsStore = userWalletsStore,
|
||||||
manageTokensUpdateFetcher,
|
manageTokensUpdateFetcher = manageTokensUpdateFetcher,
|
||||||
appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
testnetTokensStorage,
|
testnetTokensStorage = testnetTokensStorage,
|
||||||
excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
dispatchers,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,14 @@
|
||||||
package com.tangem.data.networks.single
|
package com.tangem.data.networks.single
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.blockchain.common.Blockchain
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
|
||||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
|
||||||
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
|
||||||
import com.tangem.data.tokens.utils.NetworkStatusFactory
|
import com.tangem.data.tokens.utils.NetworkStatusFactory
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
|
||||||
import com.tangem.domain.core.utils.catchOn
|
import com.tangem.domain.core.utils.catchOn
|
||||||
import com.tangem.domain.demo.DemoConfig
|
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.Network
|
|
||||||
import com.tangem.domain.tokens.model.NetworkStatus
|
import com.tangem.domain.tokens.model.NetworkStatus
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -30,27 +17,20 @@ import javax.inject.Inject
|
||||||
/**
|
/**
|
||||||
* Default implementation of [SingleNetworkStatusFetcher]
|
* Default implementation of [SingleNetworkStatusFetcher]
|
||||||
*
|
*
|
||||||
* @param excludedBlockchains excluded blockchains
|
* @property walletManagersFacade wallet managers facade
|
||||||
* @property walletManagersFacade wallet managers facade
|
* @property networksStatusesStore networks statuses store
|
||||||
* @property networksStatusesStore networks statuses store
|
* @property cardCryptoCurrencyFactory card crypto currency factory
|
||||||
* @property userWalletsStore user wallets store
|
* @property dispatchers dispatchers
|
||||||
* @property appPreferencesStore app preferences store
|
|
||||||
* @property dispatchers dispatchers
|
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val networksStatusesStore: NetworksStatusesStoreV2,
|
private val networksStatusesStore: NetworksStatusesStoreV2,
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : SingleNetworkStatusFetcher {
|
) : SingleNetworkStatusFetcher {
|
||||||
|
|
||||||
private val demoConfig = DemoConfig()
|
|
||||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig, excludedBlockchains)
|
|
||||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
|
||||||
private val networkStatusFactory = NetworkStatusFactory()
|
private val networkStatusFactory = NetworkStatusFactory()
|
||||||
|
|
||||||
override suspend fun invoke(params: SingleNetworkStatusFetcher.Params) = Either.catchOn(dispatchers.default) {
|
override suspend fun invoke(params: SingleNetworkStatusFetcher.Params) = Either.catchOn(dispatchers.default) {
|
||||||
|
|
@ -58,8 +38,10 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
||||||
networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network)
|
networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network)
|
||||||
}
|
}
|
||||||
|
|
||||||
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
|
val networkCurrencies = cardCryptoCurrencyFactory.create(
|
||||||
val networkCurrencies = createCurrencies(userWallet = userWallet, network = params.network)
|
userWalletId = params.userWalletId,
|
||||||
|
network = params.network,
|
||||||
|
)
|
||||||
|
|
||||||
val result = withContext(dispatchers.io) {
|
val result = withContext(dispatchers.io) {
|
||||||
walletManagersFacade.update(
|
walletManagersFacade.update(
|
||||||
|
|
@ -92,36 +74,4 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
||||||
Timber.e("Failed to fetch network status for $params: $it")
|
Timber.e("Failed to fetch network status for $params: $it")
|
||||||
networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network)
|
networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createCurrencies(userWallet: UserWallet, network: Network): List<CryptoCurrency> {
|
|
||||||
val blockchain = Blockchain.fromNetworkId(networkId = network.backendId)
|
|
||||||
|
|
||||||
// multi-currency wallet
|
|
||||||
if (userWallet.isMultiCurrency) return getMultiWalletCurrencies(userWallet = userWallet, network = network)
|
|
||||||
|
|
||||||
// check if the blockchain of single-currency wallet is the same as network
|
|
||||||
val cardBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
|
|
||||||
if (cardBlockchain != blockchain) return emptyList()
|
|
||||||
|
|
||||||
// single-currency wallet with token (NODL)
|
|
||||||
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
|
||||||
return cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// single-currency wallet
|
|
||||||
return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse).let(::listOf)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getMultiWalletCurrencies(userWallet: UserWallet, network: Network): List<CryptoCurrency> {
|
|
||||||
val response = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
|
||||||
key = PreferencesKeys.getUserTokensKey(userWallet.walletId.stringValue),
|
|
||||||
) ?: return emptyList()
|
|
||||||
|
|
||||||
return responseCurrenciesFactory.createCurrencies(
|
|
||||||
tokens = response.tokens.filter {
|
|
||||||
it.networkId == network.backendId && it.derivationPath == network.derivationPath.value
|
|
||||||
},
|
|
||||||
scanResponse = userWallet.scanResponse,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,8 @@ package com.tangem.data.networks.single
|
||||||
|
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.tokens.model.NetworkStatus
|
import com.tangem.domain.tokens.model.NetworkStatus
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -22,39 +22,35 @@ import org.junit.Test
|
||||||
*/
|
*/
|
||||||
internal class DefaultSingleNetworkStatusFetcherTest {
|
internal class DefaultSingleNetworkStatusFetcherTest {
|
||||||
|
|
||||||
private val walletManagersFacade: WalletManagersFacade = mockk(relaxed = true)
|
private val walletManagersFacade: WalletManagersFacade = mockk(relaxUnitFun = true)
|
||||||
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxed = true)
|
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxUnitFun = true)
|
||||||
private val userWalletsStore: UserWalletsStore = mockk(relaxed = true)
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
||||||
|
|
||||||
private val fetcher = DefaultSingleNetworkStatusFetcher(
|
private val fetcher = DefaultSingleNetworkStatusFetcher(
|
||||||
excludedBlockchains = mockk(relaxed = true),
|
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
networksStatusesStore = networksStatusesStore,
|
networksStatusesStore = networksStatusesStore,
|
||||||
userWalletsStore = userWalletsStore,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
appPreferencesStore = mockk(relaxed = true),
|
|
||||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetch network status successfully`() = runTest {
|
fun `fetch network status successfully`() = runTest {
|
||||||
val params = SingleNetworkStatusFetcher.Params(
|
val params = createParams()
|
||||||
userWalletId = userWalletId,
|
|
||||||
network = network,
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns listOf(ethereum)
|
||||||
applyRefresh = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = UpdateWalletManagerResult.MissedDerivation
|
val result = UpdateWalletManagerResult.MissedDerivation
|
||||||
coEvery { walletManagersFacade.update(userWalletId, network, emptySet()) } returns result
|
coEvery { walletManagersFacade.update(params.userWalletId, params.network, emptySet()) } returns result
|
||||||
|
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.refresh(userWalletId = userWalletId, network = network)
|
networksStatusesStore.refresh(params.userWalletId, params.network)
|
||||||
userWalletsStore.getSyncStrict(key = userWalletId)
|
cardCryptoCurrencyFactory.create(params.userWalletId, params.network)
|
||||||
walletManagersFacade.update(userWalletId, network, emptySet())
|
walletManagersFacade.update(params.userWalletId, params.network, emptySet())
|
||||||
networksStatusesStore.storeSuccess(
|
networksStatusesStore.storeSuccess(
|
||||||
userWalletId = userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
value = NetworkStatus(network, NetworkStatus.MissedDerivation),
|
value = NetworkStatus(params.network, NetworkStatus.MissedDerivation),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,21 +59,17 @@ internal class DefaultSingleNetworkStatusFetcherTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetch network status failure`() = runTest {
|
fun `fetch network status failure`() = runTest {
|
||||||
val params = SingleNetworkStatusFetcher.Params(
|
val params = createParams()
|
||||||
userWalletId = userWalletId,
|
|
||||||
network = network,
|
|
||||||
applyRefresh = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
val exception = IllegalStateException()
|
val exception = IllegalStateException()
|
||||||
coEvery { userWalletsStore.getSyncStrict(key = userWalletId) } throws exception
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } throws exception
|
||||||
|
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.refresh(userWalletId = userWalletId, network = network)
|
networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network)
|
||||||
userWalletsStore.getSyncStrict(key = userWalletId)
|
cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network)
|
||||||
networksStatusesStore.storeError(userWalletId = userWalletId, network = network)
|
networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network)
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -91,23 +83,21 @@ internal class DefaultSingleNetworkStatusFetcherTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetch network status if applyRefresh is false`() = runTest {
|
fun `fetch network status if applyRefresh is false`() = runTest {
|
||||||
val params = SingleNetworkStatusFetcher.Params(
|
val params = createParams(applyRefresh = false)
|
||||||
userWalletId = userWalletId,
|
|
||||||
network = network,
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns listOf(ethereum)
|
||||||
applyRefresh = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = UpdateWalletManagerResult.MissedDerivation
|
val result = UpdateWalletManagerResult.MissedDerivation
|
||||||
coEvery { walletManagersFacade.update(userWalletId, network, emptySet()) } returns result
|
coEvery { walletManagersFacade.update(params.userWalletId, params.network, emptySet()) } returns result
|
||||||
|
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
userWalletsStore.getSyncStrict(key = userWalletId)
|
cardCryptoCurrencyFactory.create(params.userWalletId, params.network)
|
||||||
walletManagersFacade.update(userWalletId, network, emptySet())
|
walletManagersFacade.update(params.userWalletId, params.network, emptySet())
|
||||||
networksStatusesStore.storeSuccess(
|
networksStatusesStore.storeSuccess(
|
||||||
userWalletId = userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
value = NetworkStatus(network, NetworkStatus.MissedDerivation),
|
value = NetworkStatus(params.network, NetworkStatus.MissedDerivation),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,8 +108,16 @@ internal class DefaultSingleNetworkStatusFetcherTest {
|
||||||
Truth.assertThat(actual.isRight()).isTrue()
|
Truth.assertThat(actual.isRight()).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createParams(applyRefresh: Boolean = true): SingleNetworkStatusFetcher.Params {
|
||||||
|
return SingleNetworkStatusFetcher.Params(
|
||||||
|
userWalletId = UserWalletId("011"),
|
||||||
|
network = ethereum.network,
|
||||||
|
applyRefresh = applyRefresh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val userWalletId = UserWalletId("011")
|
|
||||||
val network = MockCryptoCurrencyFactory().ethereum.network
|
val ethereum = MockCryptoCurrencyFactory().ethereum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.tokens.di
|
||||||
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.tokens.repository.*
|
import com.tangem.data.tokens.repository.*
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||||
|
|
@ -33,6 +34,7 @@ internal object TokensDataModule {
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
expressServiceLoader: ExpressServiceLoader,
|
expressServiceLoader: ExpressServiceLoader,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
): CurrenciesRepository {
|
): CurrenciesRepository {
|
||||||
return DefaultCurrenciesRepository(
|
return DefaultCurrenciesRepository(
|
||||||
tangemTechApi = tangemTechApi,
|
tangemTechApi = tangemTechApi,
|
||||||
|
|
@ -43,6 +45,7 @@ internal object TokensDataModule {
|
||||||
expressServiceLoader = expressServiceLoader,
|
expressServiceLoader = expressServiceLoader,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,6 +77,7 @@ internal object TokensDataModule {
|
||||||
cacheRegistry: CacheRegistry,
|
cacheRegistry: CacheRegistry,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
): NetworksRepository {
|
): NetworksRepository {
|
||||||
return DefaultNetworksRepository(
|
return DefaultNetworksRepository(
|
||||||
networksStatusesStore = networksStatusesStore,
|
networksStatusesStore = networksStatusesStore,
|
||||||
|
|
@ -83,6 +87,7 @@ internal object TokensDataModule {
|
||||||
cacheRegistry = cacheRegistry,
|
cacheRegistry = cacheRegistry,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import com.tangem.blockchainsdk.utils.*
|
||||||
import com.tangem.data.common.api.safeApiCall
|
import com.tangem.data.common.api.safeApiCall
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
import com.tangem.data.common.currency.*
|
import com.tangem.data.common.currency.*
|
||||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
|
||||||
import com.tangem.data.tokens.utils.CustomTokensMerger
|
import com.tangem.data.tokens.utils.CustomTokensMerger
|
||||||
import com.tangem.data.tokens.utils.UserTokensBackwardCompatibility
|
import com.tangem.data.tokens.utils.UserTokensBackwardCompatibility
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||||
|
|
@ -51,12 +50,12 @@ internal class DefaultCurrenciesRepository(
|
||||||
private val expressServiceLoader: ExpressServiceLoader,
|
private val expressServiceLoader: ExpressServiceLoader,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val excludedBlockchains: ExcludedBlockchains,
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
) : CurrenciesRepository {
|
) : CurrenciesRepository {
|
||||||
|
|
||||||
private val demoConfig = DemoConfig()
|
private val demoConfig = DemoConfig()
|
||||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
||||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig, excludedBlockchains)
|
|
||||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||||
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
|
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
|
||||||
private val customTokensMerger = CustomTokensMerger(tangemTechApi, dispatchers)
|
private val customTokensMerger = CustomTokensMerger(tangemTechApi, dispatchers)
|
||||||
|
|
@ -223,7 +222,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
val userWallet = getUserWallet(userWalletId)
|
val userWallet = getUserWallet(userWalletId)
|
||||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
||||||
|
|
||||||
val currency = cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
|
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
|
||||||
fetchExpressAssetsByNetworkIds(userWalletId, listOf(currency), refresh)
|
fetchExpressAssetsByNetworkIds(userWalletId, listOf(currency), refresh)
|
||||||
currency
|
currency
|
||||||
}
|
}
|
||||||
|
|
@ -237,8 +236,8 @@ internal class DefaultCurrenciesRepository(
|
||||||
val userWallet = getUserWallet(userWalletId)
|
val userWallet = getUserWallet(userWalletId)
|
||||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
||||||
|
|
||||||
val currencies = cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
val currencies = cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
userWallet.scanResponse,
|
scanResponse = userWallet.scanResponse,
|
||||||
)
|
)
|
||||||
fetchExpressAssetsByNetworkIds(userWalletId, currencies, refresh)
|
fetchExpressAssetsByNetworkIds(userWalletId, currencies, refresh)
|
||||||
currencies
|
currencies
|
||||||
|
|
@ -253,7 +252,9 @@ internal class DefaultCurrenciesRepository(
|
||||||
val userWallet = getUserWallet(userWalletId)
|
val userWallet = getUserWallet(userWalletId)
|
||||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
|
||||||
|
|
||||||
val currency = cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
val currency = cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
)
|
||||||
.find { it.id == id }
|
.find { it.id == id }
|
||||||
requireNotNull(currency) { "Unable to find currency with provided ID: $id" }
|
requireNotNull(currency) { "Unable to find currency with provided ID: $id" }
|
||||||
fetchExpressAssetsByNetworkIds(userWalletId, listOf(currency))
|
fetchExpressAssetsByNetworkIds(userWalletId, listOf(currency))
|
||||||
|
|
@ -672,7 +673,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
|
|
||||||
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
|
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
|
||||||
userTokensResponseFactory.createUserTokensResponse(
|
userTokensResponseFactory.createUserTokensResponse(
|
||||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
||||||
isGroupedByNetwork = false,
|
isGroupedByNetwork = false,
|
||||||
isSortedByBalance = false,
|
isSortedByBalance = false,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import com.tangem.blockchain.common.address.AddressType
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
|
||||||
import com.tangem.data.tokens.utils.NetworkStatusFactory
|
import com.tangem.data.tokens.utils.NetworkStatusFactory
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||||
import com.tangem.datasource.local.network.NetworksStatusesStore
|
import com.tangem.datasource.local.network.NetworksStatusesStore
|
||||||
|
|
@ -15,7 +15,6 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.demo.DemoConfig
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
|
@ -38,12 +37,11 @@ internal class DefaultNetworksRepository(
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val cacheRegistry: CacheRegistry,
|
private val cacheRegistry: CacheRegistry,
|
||||||
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
) : NetworksRepository {
|
) : NetworksRepository {
|
||||||
|
|
||||||
private val demoConfig = DemoConfig()
|
|
||||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig, excludedBlockchains)
|
|
||||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||||
private val networkStatusFactory = NetworkStatusFactory()
|
private val networkStatusFactory = NetworkStatusFactory()
|
||||||
|
|
||||||
|
|
@ -225,10 +223,14 @@ internal class DefaultNetworksRepository(
|
||||||
responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse).asSequence()
|
responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse).asSequence()
|
||||||
} else {
|
} else {
|
||||||
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
||||||
cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
} else {
|
} else {
|
||||||
val currency = cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
|
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
)
|
||||||
|
|
||||||
sequenceOf(currency)
|
sequenceOf(currency)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
package com.tangem.data.tokens.utils
|
|
||||||
|
|
||||||
import com.tangem.blockchain.common.Blockchain
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|
||||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
|
||||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
|
||||||
import com.tangem.domain.demo.DemoConfig
|
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
|
||||||
|
|
||||||
class CardCryptoCurrenciesFactory(
|
|
||||||
private val demoConfig: DemoConfig,
|
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
|
||||||
|
|
||||||
fun createDefaultCoinsForMultiCurrencyCard(scanResponse: ScanResponse): List<CryptoCurrency.Coin> {
|
|
||||||
val card = scanResponse.card
|
|
||||||
|
|
||||||
var blockchains = if (demoConfig.isDemoCardId(card.cardId)) {
|
|
||||||
demoConfig.demoBlockchains
|
|
||||||
} else {
|
|
||||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (card.isTestCard) {
|
|
||||||
blockchains = blockchains.mapNotNull { it.getTestnetVersion() }
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockchains.mapNotNull {
|
|
||||||
cryptoCurrencyFactory.createCoin(
|
|
||||||
blockchain = it,
|
|
||||||
extraDerivationPath = null,
|
|
||||||
scanResponse = scanResponse,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency {
|
|
||||||
val resolver = scanResponse.cardTypesResolver
|
|
||||||
val blockchain = resolver.getBlockchain()
|
|
||||||
|
|
||||||
val coin = cryptoCurrencyFactory.createCoin(
|
|
||||||
blockchain = blockchain,
|
|
||||||
extraDerivationPath = null,
|
|
||||||
scanResponse = scanResponse,
|
|
||||||
)
|
|
||||||
requireNotNull(coin) { "Coin for the single currency card cannot be null" }
|
|
||||||
|
|
||||||
val primaryToken = resolver.getPrimaryToken()?.let { token ->
|
|
||||||
cryptoCurrencyFactory.createToken(
|
|
||||||
sdkToken = token,
|
|
||||||
blockchain = blockchain,
|
|
||||||
extraDerivationPath = null,
|
|
||||||
scanResponse = scanResponse,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return primaryToken ?: coin
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCurrenciesForSingleCurrencyCardWithToken(scanResponse: ScanResponse): List<CryptoCurrency> {
|
|
||||||
val resolver = scanResponse.cardTypesResolver
|
|
||||||
val blockchain = resolver.getBlockchain()
|
|
||||||
|
|
||||||
val coin = cryptoCurrencyFactory.createCoin(
|
|
||||||
blockchain = blockchain,
|
|
||||||
extraDerivationPath = null,
|
|
||||||
scanResponse = scanResponse,
|
|
||||||
)
|
|
||||||
requireNotNull(coin) { "Coin for the single currency card cannot be null" }
|
|
||||||
|
|
||||||
val primaryToken = resolver.getPrimaryToken()?.let { token ->
|
|
||||||
cryptoCurrencyFactory.createToken(
|
|
||||||
sdkToken = token,
|
|
||||||
blockchain = blockchain,
|
|
||||||
extraDerivationPath = null,
|
|
||||||
scanResponse = scanResponse,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return listOfNotNull(coin, primaryToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue