diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/network/entity/NetworkStatusDM.kt b/core/datasource/src/main/java/com/tangem/datasource/local/network/entity/NetworkStatusDM.kt index 48a30365ee..37dfea2ebb 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/network/entity/NetworkStatusDM.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/network/entity/NetworkStatusDM.kt @@ -9,7 +9,7 @@ import java.math.BigDecimal /** * Network status for storage in the local cache. Supports two types - the [Verified] and [NoAccount]. * - * @see [com.tangem.domain.tokens.model.NetworkStatus] + * @see [com.tangem.domain.models.network.NetworkStatus] */ @JsonClass(generateAdapter = true, generator = PolymorphicAdapterType.NAME_POLYMORPHIC_ADAPTER) sealed interface NetworkStatusDM { @@ -41,8 +41,8 @@ sealed interface NetworkStatusDM { @Json(name = "derivation_path") override val derivationPath: DerivationPath, @Json(name = "selected_address") override val selectedAddress: String, @Json(name = "available_addresses") override val availableAddresses: Set
, - @Json(name = "amounts") val amounts: Map, - @Json(name = "yield_supply_statuses") val yieldSupplyStatuses: Map = emptyMap(), + @Json(name = "amounts") val amounts: List, + @Json(name = "yield_supply_statuses") val yieldSupplyStatuses: List, ) : NetworkStatusDM /** @@ -107,10 +107,44 @@ sealed interface NetworkStatusDM { } } + @JsonClass(generateAdapter = true) + data class CurrencyAmount( + @Json(name = "id") val id: CurrencyId, + @Json(name = "amount") val amount: BigDecimal, + ) + @JsonClass(generateAdapter = true) data class YieldSupplyStatus( + @Json(name = "id") val id: CurrencyId, @Json(name = "is_active") val isActive: Boolean, @Json(name = "is_initialized") val isInitialized: Boolean, @Json(name = "is_allowed_to_spend") val isAllowedToSpend: Boolean, ) + + @JsonClass(generateAdapter = true) + data class CurrencyId( + @Json(name = "value") val value: String, + ) { + + companion object Companion { + + const val CONTRACT_ADDRESS_DELIMITER = '\u2693' // ⚓ + + fun createCoinId(coinId: String): CurrencyId { + return CurrencyId(value = coinId) + } + + fun createTokenId(rawTokenId: String?, contractAddress: String): CurrencyId { + return CurrencyId( + value = buildString { + if (rawTokenId != null) { + append(rawTokenId) + } + append(CONTRACT_ADDRESS_DELIMITER) + append(contractAddress) + }, + ) + } + } + } } \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/local/network/entity/NetworkStatusDMSerializationTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/local/network/entity/NetworkStatusDMSerializationTest.kt index 4bc735b0dc..4ddef20c2c 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/local/network/entity/NetworkStatusDMSerializationTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/local/network/entity/NetworkStatusDMSerializationTest.kt @@ -64,9 +64,15 @@ class NetworkStatusDMSerializationTest { NetworkStatusDM.Address("0x123456", NetworkStatusDM.Address.Type.Primary), NetworkStatusDM.Address("0xabcdef", NetworkStatusDM.Address.Type.Secondary), ), - amounts = mapOf("ETH" to BigDecimal("1.2345")), - yieldSupplyStatuses = mapOf( - "ETH" to NetworkStatusDM.YieldSupplyStatus( + amounts = listOf( + NetworkStatusDM.CurrencyAmount( + id = NetworkStatusDM.CurrencyId.createCoinId("ethereum"), + amount = BigDecimal("1.2345"), + ), + ), + yieldSupplyStatuses = listOf( + NetworkStatusDM.YieldSupplyStatus( + id = NetworkStatusDM.CurrencyId.createCoinId("ethereum"), isActive = false, isInitialized = false, isAllowedToSpend = false, @@ -91,9 +97,15 @@ class NetworkStatusDMSerializationTest { NetworkStatusDM.Address("0x123456", NetworkStatusDM.Address.Type.Primary), NetworkStatusDM.Address("0xabcdef", NetworkStatusDM.Address.Type.Secondary), ), - amounts = mapOf("ETH" to BigDecimal("1.2345")), - yieldSupplyStatuses = mapOf( - "ETH" to NetworkStatusDM.YieldSupplyStatus( + amounts = listOf( + NetworkStatusDM.CurrencyAmount( + id = NetworkStatusDM.CurrencyId.createCoinId("ethereum"), + amount = BigDecimal("1.2345"), + ), + ), + yieldSupplyStatuses = listOf( + NetworkStatusDM.YieldSupplyStatus( + id = NetworkStatusDM.CurrencyId.createCoinId("ethereum"), isActive = false, isInitialized = false, isAllowedToSpend = false, diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/TokensOperations.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/TokensOperations.kt index b98786855f..7989830074 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/TokensOperations.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/TokensOperations.kt @@ -81,7 +81,10 @@ private fun getCurrencyIdBody(network: Network): CurrencyIdBody { rawId = network.rawId, derivationPath = path.value, ) - is Network.DerivationPath.Card, + is Network.DerivationPath.Card -> CurrencyIdBody.NetworkIdWithDerivationPath( + rawId = network.rawId, + derivationPath = path.value, + ) is Network.DerivationPath.None, -> CurrencyIdBody.NetworkId(network.rawId) } diff --git a/data/networks/build.gradle.kts b/data/networks/build.gradle.kts index d5f03f3e85..4653fb36ff 100644 --- a/data/networks/build.gradle.kts +++ b/data/networks/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { // region Project - Libs implementation(projects.libs.blockchainSdk) + implementation(tangemDeps.blockchain) { exclude(module = "joda-time") } // endregion // region DI diff --git a/data/networks/src/main/java/com/tangem/data/networks/converters/CurrencyIdConverter.kt b/data/networks/src/main/java/com/tangem/data/networks/converters/CurrencyIdConverter.kt new file mode 100644 index 0000000000..728b1db741 --- /dev/null +++ b/data/networks/src/main/java/com/tangem/data/networks/converters/CurrencyIdConverter.kt @@ -0,0 +1,98 @@ +package com.tangem.data.networks.converters + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.toCoinId +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyId +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyId.Companion.CONTRACT_ADDRESS_DELIMITER +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import com.tangem.utils.converter.TwoWayConverter +import com.tangem.domain.models.currency.CryptoCurrency.ID.Suffix as CurrencyIdSuffix + +/** + * Converts between [CurrencyId] and [CryptoCurrency.ID]. + * + * @property rawNetworkId the raw network ID associated with the currency + * @property derivationPath the derivation path used for the network + * +[REDACTED_AUTHOR] + */ +internal class CurrencyIdConverter( + private val rawNetworkId: String, + private val derivationPath: Network.DerivationPath, +) : TwoWayConverter { + + override fun convert(value: CurrencyId): CryptoCurrency.ID { + val suffixParts = value.value.split(CONTRACT_ADDRESS_DELIMITER) + + val rawId = suffixParts.getOrNull(0) + val contractAddress = suffixParts.getOrNull(1) + + return if (contractAddress.isNullOrBlank()) { + getCoinId( + coinId = rawId.takeUnless { it.isNullOrBlank() } + ?: error("Coin id is null for $rawNetworkId with $derivationPath"), + ) + } else { + getTokenId( + rawTokenId = rawId?.ifBlank { null }, + contractAddress = contractAddress, + ) + } + } + + override fun convertBack(value: CryptoCurrency.ID): CurrencyId { + return if (value.isCoin) { + CurrencyId.createCoinId( + coinId = Blockchain.fromId(value.rawNetworkId).toCoinId(), + ) + } else { + CurrencyId.createTokenId( + rawTokenId = value.rawCurrencyId?.value, + contractAddress = requireNotNull(value.contractAddress) { + "Token contractAddress is null for token id: $this" + }, + ) + } + } + + private fun getCoinId(coinId: String): CryptoCurrency.ID { + return CryptoCurrency.ID( + prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX, + body = getCurrencyIdBody(), + suffix = CurrencyIdSuffix.RawID(rawId = coinId), + ) + } + + private fun getTokenId(rawTokenId: String?, contractAddress: String): CryptoCurrency.ID { + val suffix = if (rawTokenId == null) { + CurrencyIdSuffix.ContractAddress(contractAddress) + } else { + CurrencyIdSuffix.RawID(rawTokenId, contractAddress) + } + + return CryptoCurrency.ID( + prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX, + body = getCurrencyIdBody(), + suffix = suffix, + ) + } + + private fun getCurrencyIdBody(): CryptoCurrency.ID.Body { + return when (derivationPath) { + is Network.DerivationPath.Card -> { + CryptoCurrency.ID.Body.NetworkIdWithDerivationPath( + rawId = rawNetworkId, + derivationPath = derivationPath.value, + ) + } + is Network.DerivationPath.Custom -> { + CryptoCurrency.ID.Body.NetworkIdWithDerivationPath( + rawId = rawNetworkId, + derivationPath = derivationPath.value, + ) + } + is Network.DerivationPath.None -> CryptoCurrency.ID.Body.NetworkId(rawNetworkId) + } + } +} \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkAmountsConverter.kt b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkAmountsConverter.kt index 79c210eaac..a2876c0071 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkAmountsConverter.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkAmountsConverter.kt @@ -1,35 +1,46 @@ package com.tangem.data.networks.converters +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyAmount import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkStatus import com.tangem.utils.converter.TwoWayConverter -import com.tangem.utils.extensions.mapNotNullValues -import java.math.BigDecimal -private typealias AmountsDataModel = Map +private typealias AmountsDataModel = List private typealias AmountsDomainModel = Map /** * Converter from [AmountsDataModel] to [AmountsDomainModel] and vice versa * + * @param rawNetworkId the raw network ID associated with the currency + * @param derivationPath the derivation path used for the network + * [REDACTED_AUTHOR] */ -internal object NetworkAmountsConverter : TwoWayConverter { +internal class NetworkAmountsConverter( + rawNetworkId: String, + derivationPath: Network.DerivationPath, +) : TwoWayConverter { + + private val currencyIdConverter = CurrencyIdConverter(rawNetworkId, derivationPath) override fun convert(value: AmountsDataModel): AmountsDomainModel { - return value - .mapKeys { CryptoCurrency.ID.fromValue(value = it.key) } - .mapValues { (_, amount) -> NetworkStatus.Amount.Loaded(value = amount) } + return value.associate { + val currencyId = currencyIdConverter.convert(value = it.id) + val amount = NetworkStatus.Amount.Loaded(value = it.amount) + + currencyId to amount + } } override fun convertBack(value: AmountsDomainModel): AmountsDataModel { - return value - .mapKeys { (id, _) -> id.value } - .mapNotNullValues { (_, amount) -> - when (amount) { - is NetworkStatus.Amount.Loaded -> amount.value - is NetworkStatus.Amount.NotFound -> null - } - } + return value.mapNotNull { + val amount = it.value as? NetworkStatus.Amount.Loaded ?: return@mapNotNull null + + CurrencyAmount( + id = currencyIdConverter.convertBack(value = it.key), + amount = amount.value, + ) + } } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverter.kt b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverter.kt index b142323e74..d6878c7f82 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverter.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverter.kt @@ -15,14 +15,22 @@ internal object NetworkStatusDataModelConverter : Converter { val address = NetworkAddressConverter.convertBack(value = status.address) + val amountsConverter = NetworkAmountsConverter( + rawNetworkId = value.network.rawId, + derivationPath = value.network.derivationPath, + ) + val yieldSupplyStatusConverter = NetworkYieldSupplyStatusConverter( + rawNetworkId = value.network.rawId, + derivationPath = value.network.derivationPath, + ) NetworkStatusDM.Verified( networkId = NetworkStatusDM.ID(value = value.network.rawId), derivationPath = NetworkDerivationPathConverter.convertBack(value = value.network.derivationPath), selectedAddress = address.selectedAddress, availableAddresses = address.addresses, - amounts = NetworkAmountsConverter.convertBack(value = status.amounts), - yieldSupplyStatuses = NetworkYieldSupplyStatusConverter.convertBack(status.yieldSupplyStatuses), + amounts = amountsConverter.convertBack(value = status.amounts), + yieldSupplyStatuses = yieldSupplyStatusConverter.convertBack(status.yieldSupplyStatuses), ) } is NetworkStatus.NoAccount -> { diff --git a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverter.kt b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverter.kt index d34edbbe5b..4dddbb56bc 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverter.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverter.kt @@ -2,45 +2,43 @@ package com.tangem.data.networks.converters import com.tangem.datasource.local.network.entity.NetworkStatusDM import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network import com.tangem.domain.models.yield.supply.YieldSupplyStatus import com.tangem.utils.converter.TwoWayConverter -import com.tangem.utils.extensions.mapNotNullValues -private typealias YieldSupplyStatusDataModel = Map +private typealias YieldSupplyStatusDataModel = List private typealias YieldSupplyStatusDomainModel = Map -internal object NetworkYieldSupplyStatusConverter : - TwoWayConverter { +internal class NetworkYieldSupplyStatusConverter( + rawNetworkId: String, + derivationPath: Network.DerivationPath, +) : TwoWayConverter { + + private val currencyIdConverter = CurrencyIdConverter(rawNetworkId, derivationPath) override fun convert(value: YieldSupplyStatusDataModel): YieldSupplyStatusDomainModel { - return value - .mapKeys { CryptoCurrency.ID.fromValue(value = it.key) } - .mapValues { (_, yieldSupplyStatus) -> - if (yieldSupplyStatus != null) { - YieldSupplyStatus( - isActive = yieldSupplyStatus.isActive, - isInitialized = yieldSupplyStatus.isInitialized, - isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend, - ) - } else { - null - } - } + return value.associate { + val id = currencyIdConverter.convert(value = it.id) + val status = YieldSupplyStatus( + isActive = it.isActive, + isInitialized = it.isInitialized, + isAllowedToSpend = it.isAllowedToSpend, + ) + + id to status + } } override fun convertBack(value: YieldSupplyStatusDomainModel): YieldSupplyStatusDataModel { - return value - .mapKeys { (id, _) -> id.value } - .mapNotNullValues { (_, yieldSupplyStatus) -> - if (yieldSupplyStatus != null) { - NetworkStatusDM.YieldSupplyStatus( - isActive = yieldSupplyStatus.isActive, - isInitialized = yieldSupplyStatus.isInitialized, - isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend, - ) - } else { - null - } - } + return value.mapNotNull { (currencyId, yieldSupplyStatus) -> + if (yieldSupplyStatus == null) return@mapNotNull null + + NetworkStatusDM.YieldSupplyStatus( + id = currencyIdConverter.convertBack(value = currencyId), + isActive = yieldSupplyStatus.isActive, + isInitialized = yieldSupplyStatus.isInitialized, + isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend, + ) + } } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverter.kt b/data/networks/src/main/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverter.kt index 40a48b378f..287e82b3fa 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverter.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverter.kt @@ -22,14 +22,26 @@ internal object SimpleNetworkStatusConverter : Converter { NetworkStatus.Verified( address = address, - amounts = NetworkAmountsConverter.convert(value = value.amounts), + amounts = amountsConverter.convert(value = value.amounts), pendingTransactions = emptyMap(), source = StatusSource.CACHE, - yieldSupplyStatuses = NetworkYieldSupplyStatusConverter.convert(value = value.yieldSupplyStatuses), + yieldSupplyStatuses = yieldSupplyStatusConverter.convert(value = value.yieldSupplyStatuses), ) } is NetworkStatusDM.NoAccount -> { diff --git a/data/networks/src/main/java/com/tangem/data/networks/di/NetworkDataModule.kt b/data/networks/src/main/java/com/tangem/data/networks/di/NetworkDataModule.kt index e2f4c82b9c..43648cfd89 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/di/NetworkDataModule.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/di/NetworkDataModule.kt @@ -40,6 +40,7 @@ internal object NetworkDataModule { dispatchers: CoroutineDispatcherProvider, ): NetworksStatusesStore { return DefaultNetworksStatusesStore( + context = context, runtimeStore = RuntimeSharedStore(), persistenceDataStore = DataStoreFactory.create( serializer = MoshiDataStoreSerializer( @@ -47,7 +48,7 @@ internal object NetworkDataModule { types = mapWithStringKeyTypes(valueTypes = setTypes()), defaultValue = emptyMap(), ), - produceFile = { context.dataStoreFile(fileName = "networks_statuses") }, + produceFile = { context.dataStoreFile(fileName = "networks_statuses_2") }, scope = CoroutineScope(context = dispatchers.io + SupervisorJob()), ), dispatchers = dispatchers, diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt index 213a73be50..9cba360e2e 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt @@ -1,5 +1,6 @@ package com.tangem.data.networks.store +import android.content.Context import androidx.datastore.core.DataStore import com.tangem.data.networks.converters.NetworkStatusDataModelConverter import com.tangem.data.networks.converters.SimpleNetworkStatusConverter @@ -20,6 +21,7 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import timber.log.Timber +import java.io.File internal typealias WalletIdWithSimpleStatus = Map> internal typealias WalletIdWithStatusDM = Map> @@ -27,11 +29,13 @@ internal typealias WalletIdWithStatusDM = Map> /** * Default implementation of [NetworksStatusesStore] * + * @param context context * @property runtimeStore runtime store * @property persistenceDataStore persistence store * @param dispatchers dispatchers */ internal class DefaultNetworksStatusesStore( + context: Context, private val runtimeStore: RuntimeSharedStore, private val persistenceDataStore: DataStore, dispatchers: CoroutineDispatcherProvider, @@ -41,6 +45,16 @@ internal class DefaultNetworksStatusesStore( init { scope.launch { + try { + val oldFile = File(context.filesDir, "datastore/networks_statuses") + + if (oldFile.exists()) { + oldFile.delete() + } + } catch (e: Exception) { + Timber.e(e, "Error while deleting old networks statuses datastore file") + } + val cachedStatuses = persistenceDataStore.data.firstOrNull() ?: return@launch runtimeStore.store( diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/CurrencyIdConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/CurrencyIdConverterTest.kt new file mode 100644 index 0000000000..dbc7712de6 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/CurrencyIdConverterTest.kt @@ -0,0 +1,190 @@ +package com.tangem.data.networks.converters + +import com.google.common.truth.Truth +import com.tangem.common.test.utils.ProvideTestModels +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyId +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class CurrencyIdConverterTest { + + private val rawNetworkId = "ETH" + private val derivationPath = Network.DerivationPath.Card(value = "m/44'/60'/0'/0/0") + private val derivationPathHashCode = "-1843072795" + private val converter = CurrencyIdConverter(rawNetworkId = rawNetworkId, derivationPath = derivationPath) + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class Convert { + + @ParameterizedTest + @ProvideTestModels + fun convert(model: ConvertModel) { + // Act + val actual = runCatching { converter.convert(value = model.value) } + + // Assert + actual + .onSuccess { + Truth.assertThat(it).isEqualTo(model.expected.getOrNull()) + } + .onFailure { + val expected = model.expected.exceptionOrNull()!! + + Truth.assertThat(it).isInstanceOf(expected::class.java) + Truth.assertThat(it).hasMessageThat().isEqualTo(expected.message) + } + } + + private fun provideTestModels(): Collection = listOf( + // create coin id + ConvertModel( + value = CurrencyId.createCoinId("ethereum"), + expected = Result.success( + CryptoCurrency.ID.fromValue(value = "coin⟨ETH→$derivationPathHashCode⟩ethereum"), + ), + ), + ConvertModel( + value = CurrencyId.createCoinId(""), + expected = Result.failure( + IllegalStateException("Coin id is null for $rawNetworkId with $derivationPath"), + ), + ), + ConvertModel( + value = CurrencyId.createCoinId(" "), + expected = Result.failure( + IllegalStateException("Coin id is null for $rawNetworkId with $derivationPath"), + ), + ), + // create token id + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩usdt⚓0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = null, + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = "", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = " ", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = "", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue(value = "coin⟨ETH→$derivationPathHashCode⟩usdt"), + ), + ), + ConvertModel( + value = CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = " ", + ), + expected = Result.success( + CryptoCurrency.ID.fromValue(value = "coin⟨ETH→$derivationPathHashCode⟩usdt"), + ), + ), + ) + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class ConvertBack { + + @ParameterizedTest + @ProvideTestModels + fun convertBack(model: ConvertBackModel) { + // Act + val actual = runCatching { converter.convertBack(value = model.value) } + + // Assert + actual + .onSuccess { + Truth.assertThat(it).isEqualTo(model.expected.getOrNull()) + } + .onFailure { + val expected = model.expected.exceptionOrNull()!! + + Truth.assertThat(it).isInstanceOf(expected::class.java) + Truth.assertThat(it).hasMessageThat().isEqualTo(expected.message) + } + } + + private fun provideTestModels(): Collection = listOf( + ConvertBackModel( + value = CryptoCurrency.ID.fromValue("coin⟨ETH→$derivationPathHashCode⟩ethereum"), + expected = Result.success( + CurrencyId.createCoinId("ethereum"), + ), + ), + ConvertBackModel( + value = CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩usdt⚓0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ConvertBackModel( + value = CryptoCurrency.ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + expected = Result.success( + CurrencyId.createTokenId( + rawTokenId = null, + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + ), + ), + ) + } + + data class ConvertModel(val value: CurrencyId, val expected: Result) + + data class ConvertBackModel(val value: CryptoCurrency.ID, val expected: Result) +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAddressConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAddressConverterTest.kt index 591f85f78d..d4866eeabf 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAddressConverterTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAddressConverterTest.kt @@ -1,16 +1,17 @@ package com.tangem.data.networks.converters import com.google.common.truth.Truth +import com.tangem.common.test.utils.ProvideTestModels import com.tangem.datasource.local.network.entity.NetworkStatusDM import com.tangem.domain.models.network.NetworkAddress import org.junit.jupiter.api.Nested import org.junit.jupiter.api.TestInstance import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource /** [REDACTED_AUTHOR] */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class NetworkAddressConverterTest { @Nested @@ -18,7 +19,7 @@ internal class NetworkAddressConverterTest { inner class Convert { @ParameterizedTest - @MethodSource("provideTestModels") + @ProvideTestModels fun convert(model: ConvertModel) { // Act val actual = runCatching { NetworkAddressConverter.convert(value = model.value) } @@ -190,7 +191,7 @@ internal class NetworkAddressConverterTest { inner class ConvertBack { @ParameterizedTest - @MethodSource("provideTestModels") + @ProvideTestModels fun convertBack(model: ConvertBackModel) { // Act val actual = NetworkAddressConverter.convertBack(value = model.value) diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAmountsConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAmountsConverterTest.kt index 38dfb7d057..5bbe4daf2d 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAmountsConverterTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkAmountsConverterTest.kt @@ -1,77 +1,99 @@ package com.tangem.data.networks.converters import com.google.common.truth.Truth +import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyId import com.tangem.domain.models.currency.CryptoCurrency.ID -import com.tangem.domain.models.currency.CryptoCurrency.ID.Body -import com.tangem.domain.models.currency.CryptoCurrency.ID.Prefix -import com.tangem.domain.models.network.NetworkStatus.Amount +import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkStatus.Amount.Loaded import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance import java.math.BigDecimal /** [REDACTED_AUTHOR] */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class NetworkAmountsConverterTest { + private val rawNetworkId = "ETH" + private val derivationPath = Network.DerivationPath.Card(value = "m/44'/60'/0'/0/0") + private val derivationPathHashCode = "-1843072795" + private val converter = NetworkAmountsConverter(rawNetworkId = rawNetworkId, derivationPath = derivationPath) + @Test fun convert() { // Arrange - val value = mapOf( - "coin⟨BCH⟩bitcoin-cash" to BigDecimal.ZERO, - "coin⟨ETH→12367123⟩ethereum" to BigDecimal.ONE, + val value = listOf( + NetworkStatusDM.CurrencyAmount(CurrencyId.createCoinId(coinId = "ethereum"), BigDecimal.ONE), + NetworkStatusDM.CurrencyAmount( + id = CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + amount = BigDecimal.ZERO, + ), + NetworkStatusDM.CurrencyAmount( + id = CurrencyId.createTokenId( + rawTokenId = null, + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + amount = BigDecimal.TEN, + ), ) // Act - val actual = NetworkAmountsConverter.convert(value) + val actual = converter.convert(value) // Assert val expected = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BCH"), - suffix = ID.Suffix.RawID(rawId = "bitcoin-cash"), + ID.fromValue("coin⟨ETH→$derivationPathHashCode⟩ethereum") to Loaded(value = BigDecimal.ONE), + ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩usdt⚓0xdAC17F958D2ee523a2206206994597C13D831ec7", ) to Loaded(value = BigDecimal.ZERO), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkIdWithDerivationPath(rawId = "ETH", derivationPathHashCode = 12367123), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to Loaded(value = BigDecimal.ONE), + ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ) to Loaded(value = BigDecimal.TEN), ) - Truth.assertThat(actual).isEqualTo(expected) + Truth.assertThat(actual).containsExactlyEntriesIn(expected) } @Test fun convertBack() { // Arrange val value = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BCH"), - suffix = ID.Suffix.RawID(rawId = "bitcoin-cash"), + ID.fromValue("coin⟨ETH→$derivationPathHashCode⟩ethereum") to Loaded(value = BigDecimal.ONE), + ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩usdt⚓0xdAC17F958D2ee523a2206206994597C13D831ec7", ) to Loaded(value = BigDecimal.ZERO), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkIdWithDerivationPath(rawId = "ETH", derivationPathHashCode = 12367123), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to Loaded(value = BigDecimal.ONE), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BTC"), - suffix = ID.Suffix.RawID(rawId = "bitcoin"), - ) to Amount.NotFound, + ID.fromValue( + value = "token⟨ETH→$derivationPathHashCode⟩0xdAC17F958D2ee523a2206206994597C13D831ec7", + ) to Loaded(value = BigDecimal.TEN), ) // Act - val actual = NetworkAmountsConverter.convertBack(value) + val actual = converter.convertBack(value) // Assert - val expected = mapOf( - "coin⟨BCH⟩bitcoin-cash" to BigDecimal.ZERO, - "coin⟨ETH→12367123⟩ethereum" to BigDecimal.ONE, + val expected = listOf( + NetworkStatusDM.CurrencyAmount(CurrencyId.createCoinId(coinId = "ethereum"), BigDecimal.ONE), + NetworkStatusDM.CurrencyAmount( + id = CurrencyId.createTokenId( + rawTokenId = "usdt", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + amount = BigDecimal.ZERO, + ), + NetworkStatusDM.CurrencyAmount( + id = CurrencyId.createTokenId( + rawTokenId = null, + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ), + amount = BigDecimal.TEN, + ), ) - Truth.assertThat(actual).isEqualTo(expected) + Truth.assertThat(actual).containsExactlyElementsIn(expected) } } \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverterTest.kt index a254164b1a..0d16b0ac0a 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverterTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkStatusDataModelConverterTest.kt @@ -2,7 +2,9 @@ package com.tangem.data.networks.converters import com.google.common.truth.Truth import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.utils.ProvideTestModels import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.datasource.local.network.entity.NetworkStatusDM.* import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency.ID import com.tangem.domain.models.currency.CryptoCurrency.ID.Body @@ -14,7 +16,6 @@ import com.tangem.domain.models.network.NetworkStatus.Amount import com.tangem.domain.models.yield.supply.YieldSupplyStatus import org.junit.jupiter.api.TestInstance import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource import java.math.BigDecimal /** @@ -26,7 +27,7 @@ internal class NetworkStatusDataModelConverterTest { private val network: Network = MockCryptoCurrencyFactory().ethereum.network @ParameterizedTest - @MethodSource("provideTestModels") + @ProvideTestModels fun convert(model: ConvertModel) { // Act val actual = NetworkStatusDataModelConverter.convert(value = model.value) @@ -48,11 +49,7 @@ internal class NetworkStatusDataModelConverterTest { ), ), amounts = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BCH"), - suffix = ID.Suffix.RawID(rawId = "bitcoin-cash"), - ) to Amount.Loaded(value = BigDecimal.ZERO), + ID.fromValue(value = "coin⟨ETH→0⟩ethereum") to Amount.Loaded(value = BigDecimal.ZERO), ID( prefix = Prefix.COIN_PREFIX, body = Body.NetworkId(rawId = "BTC"), @@ -61,11 +58,7 @@ internal class NetworkStatusDataModelConverterTest { ), pendingTransactions = mapOf(), // doesn't matter yieldSupplyStatuses = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BCH"), - suffix = ID.Suffix.RawID(rawId = "bitcoin-cash"), - ) to YieldSupplyStatus( + ID.fromValue(value = "token⟨ETH→0⟩usdt⚓0x1") to YieldSupplyStatus( isActive = false, isInitialized = false, isAllowedToSpend = false, @@ -79,22 +72,25 @@ internal class NetworkStatusDataModelConverterTest { source = StatusSource.ACTUAL, // doesn't matter ), ), - expected = NetworkStatusDM.Verified( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + expected = Verified( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "", - type = NetworkStatusDM.DerivationPath.Type.NONE, + type = DerivationPath.Type.NONE, ), selectedAddress = "0x123", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x123", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), ), - amounts = mapOf("coin⟨BCH⟩bitcoin-cash" to BigDecimal.ZERO), - yieldSupplyStatuses = mapOf( - "coin⟨BCH⟩bitcoin-cash" to NetworkStatusDM.YieldSupplyStatus( + amounts = listOf( + CurrencyAmount(CurrencyId.createCoinId("ethereum"), BigDecimal.ZERO), + ), + yieldSupplyStatuses = listOf( + YieldSupplyStatus( + id = CurrencyId.createTokenId("usdt", "0x1"), isActive = false, isInitialized = false, isAllowedToSpend = false, @@ -120,17 +116,17 @@ internal class NetworkStatusDataModelConverterTest { source = StatusSource.ACTUAL, // doesn't matter ), ), - expected = NetworkStatusDM.NoAccount( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + expected = NoAccount( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "", - type = NetworkStatusDM.DerivationPath.Type.NONE, + type = DerivationPath.Type.NONE, ), selectedAddress = "0x123", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x123", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), ), amountToCreateAccount = BigDecimal.ONE, diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverterTest.kt index c1ef31d529..1c21deff5d 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverterTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/NetworkYieldSupplyStatusConverterTest.kt @@ -2,82 +2,74 @@ package com.tangem.data.networks.converters import com.google.common.truth.Truth import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.datasource.local.network.entity.NetworkStatusDM.CurrencyId import com.tangem.domain.models.currency.CryptoCurrency.ID -import com.tangem.domain.models.currency.CryptoCurrency.ID.Body -import com.tangem.domain.models.currency.CryptoCurrency.ID.Prefix +import com.tangem.domain.models.network.Network import com.tangem.domain.models.yield.supply.YieldSupplyStatus import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +@TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class NetworkYieldSupplyStatusConverterTest { + private val rawNetworkId = "ETH" + private val derivationPath = Network.DerivationPath.Card(value = "m/44'/60'/0'/0/0") + private val derivationPathHashCode = "-1843072795" + private val converter = NetworkYieldSupplyStatusConverter(rawNetworkId, derivationPath) + + private val domainStatus = YieldSupplyStatus( + isActive = true, + isInitialized = true, + isAllowedToSpend = true, + ) + @Test fun convert() { // Arrange - val value = mapOf( - "coin⟨ETH⟩ethereum" to NetworkStatusDM.YieldSupplyStatus( - isActive = false, - isInitialized = false, - isAllowedToSpend = false, - ), - "coin⟨ETH→12367123⟩ethereum" to null, + val value = listOf( + createDataStatus(id = CurrencyId.createCoinId("ethereum")), + createDataStatus(id = CurrencyId.createTokenId("usdt", "0x1")), ) // Act - val actual = NetworkYieldSupplyStatusConverter.convert(value) + val actual = converter.convert(value) // Assert val expected = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "ETH"), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to YieldSupplyStatus( - isActive = false, - isInitialized = false, - isAllowedToSpend = false, - ), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkIdWithDerivationPath(rawId = "ETH", derivationPathHashCode = 12367123), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to null, + ID.fromValue("coin⟨ETH→$derivationPathHashCode⟩ethereum") to domainStatus, + ID.fromValue("token⟨ETH→$derivationPathHashCode⟩usdt⚓0x1") to domainStatus, ) - Truth.assertThat(actual).isEqualTo(expected) + Truth.assertThat(actual).containsExactlyEntriesIn(expected) } @Test fun convertBack() { // Arrange val value = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "ETH"), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to YieldSupplyStatus( - isActive = false, - isInitialized = false, - isAllowedToSpend = false, - ), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkIdWithDerivationPath(rawId = "ETH", derivationPathHashCode = 12367123), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to null, + ID.fromValue("coin⟨ETH→$derivationPathHashCode⟩ethereum") to domainStatus, + ID.fromValue("token⟨ETH→$derivationPathHashCode⟩usdt⚓0x1") to domainStatus, + ID.fromValue("token⟨ETH→$derivationPathHashCode⟩usdc⚓0x1") to null, ) // Act - val actual = NetworkYieldSupplyStatusConverter.convertBack(value) + val actual = converter.convertBack(value) // Assert - val expected = mapOf( - "coin⟨ETH⟩ethereum" to NetworkStatusDM.YieldSupplyStatus( - isActive = false, - isInitialized = false, - isAllowedToSpend = false, - ), + val expected = listOf( + createDataStatus(id = CurrencyId.createCoinId("ethereum")), + createDataStatus(id = CurrencyId.createTokenId("usdt", "0x1")), ) - Truth.assertThat(actual).isEqualTo(expected) + Truth.assertThat(actual).containsExactlyElementsIn(expected) + } + + private fun createDataStatus(id: CurrencyId): NetworkStatusDM.YieldSupplyStatus { + return NetworkStatusDM.YieldSupplyStatus( + id = id, + isActive = true, + isInitialized = true, + isAllowedToSpend = true, + ) } } \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverterTest.kt b/data/networks/src/test/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverterTest.kt index 3fe7708a7e..2ca9c9e06c 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverterTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/converters/SimpleNetworkStatusConverterTest.kt @@ -2,12 +2,12 @@ package com.tangem.data.networks.converters import com.google.common.truth.Truth import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.utils.ProvideTestModels import com.tangem.data.networks.models.SimpleNetworkStatus import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.datasource.local.network.entity.NetworkStatusDM.* import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency.ID -import com.tangem.domain.models.currency.CryptoCurrency.ID.Body -import com.tangem.domain.models.currency.CryptoCurrency.ID.Prefix import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkStatus @@ -15,7 +15,6 @@ import com.tangem.domain.models.network.NetworkStatus.Amount import com.tangem.domain.models.yield.supply.YieldSupplyStatus import org.junit.jupiter.api.TestInstance import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource import java.math.BigDecimal /** @@ -27,7 +26,7 @@ internal class SimpleNetworkStatusConverterTest { private val network: Network = MockCryptoCurrencyFactory().ethereum.network @ParameterizedTest - @MethodSource("provideTestModels") + @ProvideTestModels fun convert(model: ConvertModel) { // Act val actual = runCatching { SimpleNetworkStatusConverter.convert(value = model.value) } @@ -48,34 +47,28 @@ internal class SimpleNetworkStatusConverterTest { private fun provideTestModels() = listOf( // region Verified ConvertModel( - value = NetworkStatusDM.Verified( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = Verified( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf( - NetworkStatusDM.Address( - value = "0x1", - type = NetworkStatusDM.Address.Type.Primary, - ), - NetworkStatusDM.Address( - value = "0x2", - type = NetworkStatusDM.Address.Type.Secondary, - ), + Address(value = "0x1", type = Address.Type.Primary), + Address(value = "0x2", type = Address.Type.Secondary), ), - amounts = mapOf( - "coin⟨BCH⟩bitcoin-cash" to BigDecimal.ZERO, - "coin⟨ETH→12367123⟩ethereum" to BigDecimal.ONE, + amounts = listOf( + CurrencyAmount(CurrencyId.createCoinId("ethereum"), BigDecimal.ZERO), + CurrencyAmount(CurrencyId.createTokenId("usdt", "0x1"), BigDecimal.ZERO), ), - yieldSupplyStatuses = mapOf( - "coin⟨ETH⟩ethereum" to NetworkStatusDM.YieldSupplyStatus( + yieldSupplyStatuses = listOf( + YieldSupplyStatus( + id = CurrencyId.createCoinId("ethereum"), isActive = false, isInitialized = false, isAllowedToSpend = false, ), - "coin⟨ETH⟩ethereum" to null, ), ), expected = SimpleNetworkStatus( @@ -101,33 +94,16 @@ internal class SimpleNetworkStatusConverterTest { ), ), amounts = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "BCH"), - suffix = ID.Suffix.RawID(rawId = "bitcoin-cash"), - ) to Amount.Loaded(value = BigDecimal.ZERO), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkIdWithDerivationPath(rawId = "ETH", derivationPathHashCode = 12367123), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to Amount.Loaded(value = BigDecimal.ONE), + ID.fromValue("coin⟨ETH→3046160⟩ethereum") to Amount.Loaded(value = BigDecimal.ZERO), + ID.fromValue("token⟨ETH→3046160⟩usdt⚓0x1") to Amount.Loaded(value = BigDecimal.ZERO), ), pendingTransactions = emptyMap(), yieldSupplyStatuses = mapOf( - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "ETH"), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to YieldSupplyStatus( + ID.fromValue("coin⟨ETH→3046160⟩ethereum") to YieldSupplyStatus( isActive = false, isInitialized = false, isAllowedToSpend = false, ), - ID( - prefix = Prefix.COIN_PREFIX, - body = Body.NetworkId(rawId = "ETH"), - suffix = ID.Suffix.RawID(rawId = "ethereum"), - ) to null, ), source = StatusSource.CACHE, ), @@ -137,21 +113,21 @@ internal class SimpleNetworkStatusConverterTest { // region NoAccount ConvertModel( - value = NetworkStatusDM.NoAccount( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = NoAccount( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x1", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), - NetworkStatusDM.Address( + Address( value = "0x2", - type = NetworkStatusDM.Address.Type.Secondary, + type = Address.Type.Secondary, ), ), amountToCreateAccount = BigDecimal.ONE, @@ -189,83 +165,83 @@ internal class SimpleNetworkStatusConverterTest { // region Error ConvertModel( - value = NetworkStatusDM.Verified( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = Verified( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x2", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), ), - amounts = emptyMap(), - yieldSupplyStatuses = emptyMap(), + amounts = emptyList(), + yieldSupplyStatuses = emptyList(), ), expected = Result.failure( exception = IllegalArgumentException("Selected address must not be null"), ), ), ConvertModel( - value = NetworkStatusDM.Verified( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = Verified( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf(), - amounts = emptyMap(), - yieldSupplyStatuses = emptyMap(), + amounts = emptyList(), + yieldSupplyStatuses = emptyList(), ), expected = Result.failure( exception = IllegalArgumentException("Selected address must not be null"), ), ), ConvertModel( - value = NetworkStatusDM.Verified( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = Verified( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x1", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), - NetworkStatusDM.Address( + Address( value = "0x2", - type = NetworkStatusDM.Address.Type.Secondary, + type = Address.Type.Secondary, ), ), - amounts = emptyMap(), - yieldSupplyStatuses = emptyMap(), + amounts = emptyList(), + yieldSupplyStatuses = emptyList(), ), expected = Result.failure( exception = IllegalArgumentException("Selected address must not be null"), ), ), ConvertModel( - value = NetworkStatusDM.NoAccount( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = NoAccount( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x1", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), - NetworkStatusDM.Address( + Address( value = "0x2", - type = NetworkStatusDM.Address.Type.Secondary, + type = Address.Type.Secondary, ), ), amountToCreateAccount = BigDecimal.ONE, @@ -276,17 +252,17 @@ internal class SimpleNetworkStatusConverterTest { ), ), ConvertModel( - value = NetworkStatusDM.NoAccount( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = NoAccount( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf( - NetworkStatusDM.Address( + Address( value = "0x2", - type = NetworkStatusDM.Address.Type.Primary, + type = Address.Type.Primary, ), ), amountToCreateAccount = BigDecimal.ONE, @@ -297,11 +273,11 @@ internal class SimpleNetworkStatusConverterTest { ), ), ConvertModel( - value = NetworkStatusDM.NoAccount( - networkId = NetworkStatusDM.ID(network.rawId), - derivationPath = NetworkStatusDM.DerivationPath( + value = NoAccount( + networkId = ID(network.rawId), + derivationPath = DerivationPath( value = "card", - type = NetworkStatusDM.DerivationPath.Type.CARD, + type = DerivationPath.Type.CARD, ), selectedAddress = "0x1", availableAddresses = setOf(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt index dfed3c800b..17c16671d5 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt @@ -11,6 +11,7 @@ import com.tangem.data.networks.toSimple import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test @@ -23,6 +24,7 @@ internal class GetTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt index 3c979ceb29..63d2daa46b 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt @@ -30,6 +30,7 @@ internal class InitializationTest { every { persistenceStore.data } returns emptyFlow() DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), @@ -44,6 +45,7 @@ internal class InitializationTest { val persistenceStore = MockStateDataStore(default = emptyMap()) DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), @@ -66,6 +68,7 @@ internal class InitializationTest { } DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt index 846bf9decb..d310e1bdde 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt @@ -11,6 +11,7 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -27,6 +28,7 @@ internal class ParameterizedStoreStatusTest(private val model: Model) { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt index adae5ca5c7..7e62ff9843 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt @@ -11,6 +11,7 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -27,6 +28,7 @@ internal class ParameterizedStoreSuccessTest(private val model: Model) { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt index 60e055e9ec..99436f876e 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt @@ -10,6 +10,7 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -26,6 +27,7 @@ internal class ParameterizedStoreTest(private val model: Model) { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt index f023eb27a1..cfb2eb16f0 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt @@ -11,6 +11,7 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -24,6 +25,7 @@ internal class SetSourceAsCacheTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt index 655bf8d83d..1136ce2921 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt @@ -11,6 +11,7 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -24,6 +25,7 @@ internal class SetSourceAsOnlyCacheTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt index 4c75c7b7c0..aa545d3ba4 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt @@ -11,6 +11,7 @@ import com.tangem.datasource.local.network.entity.NetworkStatusDM import com.tangem.domain.models.StatusSource import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -24,6 +25,7 @@ internal class StoreStatusTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt index e1177104a8..ebf54b27c7 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt @@ -10,6 +10,7 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.StatusSource import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -23,6 +24,7 @@ internal class StoreSuccessTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt index 3f1c2ad5eb..eb5cd705b3 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt @@ -10,6 +10,7 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.StatusSource import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -23,6 +24,7 @@ internal class StoreTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt index e4114806e8..785c1b8d1b 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt @@ -14,6 +14,7 @@ import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest import org.junit.Test @@ -27,6 +28,7 @@ internal class UpdateStatusSourceTest { private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultNetworksStatusesStore( + context = mockk(), runtimeStore = runtimeStore, persistenceDataStore = persistenceStore, dispatchers = TestingCoroutineDispatcherProvider(), diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrency.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrency.kt index 3e745dc152..31a759a3c0 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrency.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrency.kt @@ -108,6 +108,10 @@ sealed class CryptoCurrency { is Body.NetworkIdWithDerivationPath -> body.rawId } + /** Flag indicating whether the cryptocurrency is a coin */ + val isCoin: Boolean + get() = prefix == Prefix.COIN_PREFIX + /** * Represents the different types of prefixes that can be associated with a cryptocurrency ID. * These prefixes can help in quickly categorizing the type of cryptocurrency. @@ -131,16 +135,16 @@ sealed class CryptoCurrency { /** The value of the body. */ abstract val value: String + /** + * Represents a raw network ID. + * Should be used for cryptocurrencies that do not support a derivation path. + */ @Serializable - /** Represents a raw network ID. */ data class NetworkId(val rawId: String) : Body() { override val value: String get() = rawId } - /** - * Represents a raw network ID with a network derivation path. - * Should be used for a cryptocurrencies with custom derivation path. - */ + /** Represents a raw network ID with a network derivation path */ @Serializable data class NetworkIdWithDerivationPath( val rawId: String, @@ -227,9 +231,9 @@ sealed class CryptoCurrency { * Example: * 1. coin⟨BCH⟩bitcoin-cash * 2. coin⟨ETH→12367123⟩ethereum + * 3. token⟨ETH→12367123⟩usdt⚓0xdAC17F958D2ee523a2206206994597C13D831ec7 */ fun fromValue(value: String): ID { - // ID(value='coin⟨BCH⟩bitcoin-cash' val parts = value.split(PREFIX_DELIMITER, SUFFIX_DELIMITER) require(value = parts.size == ID_PARTS_COUNT) { "Invalid ID format: $value" }