Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-23 11:16:28 +03:00
commit ca49abf2b1
892 changed files with 17979 additions and 6555 deletions

View file

@ -12,6 +12,7 @@ android {
dependencies {
/* Core */
implementation(projects.core.decompose)
implementation(projects.core.configToggles)
/* Domain */
implementation(projects.domain.qrScanning.models)

View file

@ -7,10 +7,10 @@ import com.tangem.common.routing.entity.SerializableIntent
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.serialization.Serializable
@ -122,7 +122,7 @@ sealed class AppRoute(val path: String) : Route {
}
@Serializable
data object WalletConnectSessions : AppRoute(path = "/wallet_connect_sessions")
data class WalletConnectSessions(val userWalletId: UserWalletId) : AppRoute(path = "/wallet_connect_sessions")
@Serializable
data class QrScanning(val source: Source) : AppRoute(path = "/$source/qr_scanning${source.path}") {

View file

@ -0,0 +1,35 @@
package com.tangem.common.routing
sealed class DeepLinkRoute {
abstract val host: String
data object Onramp : DeepLinkRoute() {
override val host: String = "onramp"
}
data object Sell : DeepLinkRoute() {
override val host: String = "redirect_sell"
}
data object Buy : DeepLinkRoute() {
override val host: String = "redirect"
}
data object Referral : DeepLinkRoute() {
override val host: String = "referral"
}
data object Wallet : DeepLinkRoute() {
override val host: String = "main"
}
data object TokenDetails : DeepLinkRoute() {
override val host: String = "token"
}
}
enum class DeepLinkScheme(val scheme: String) {
Tangem(scheme = "tangem"),
WalletConnect(scheme = "wc"),
}

View file

@ -0,0 +1,11 @@
package com.tangem.common.routing
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
class RoutingFeatureToggle(
private val featureTogglesManager: FeatureTogglesManager,
) {
val isDeepLinkNavigationEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "DEEPLINK_NAVIGATION_ENABLED")
}

View file

@ -19,6 +19,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.staking.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
@ -30,4 +31,6 @@ dependencies {
implementation(tangemDeps.blockchain)
implementation(tangemDeps.card.core)
implementation(deps.test.junit5)
}

View file

@ -2,9 +2,9 @@ package com.tangem.common.test.domain.network
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import java.math.BigDecimal
/**
@ -14,36 +14,41 @@ object MockNetworkStatusFactory {
private val defaultNetwork = MockCryptoCurrencyFactory().ethereum.network
fun createVerified(network: Network = defaultNetwork): NetworkStatus {
fun createVerified(
network: Network = defaultNetwork,
source: StatusSource = StatusSource.ACTUAL,
transform: (NetworkStatus.Verified) -> NetworkStatus.Verified = { it },
): NetworkStatus {
return NetworkStatus(
network = network,
value = NetworkStatus.Verified(
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(
value = "0x123",
value = "0x1",
type = NetworkAddress.Address.Type.Primary,
),
),
amounts = mapOf(),
pendingTransactions = mapOf(),
source = StatusSource.ACTUAL,
),
source = source,
)
.let(transform),
)
}
fun createNoAccount(network: Network = defaultNetwork): NetworkStatus {
fun createNoAccount(network: Network = defaultNetwork, source: StatusSource = StatusSource.ACTUAL): NetworkStatus {
return NetworkStatus(
network = network,
value = NetworkStatus.NoAccount(
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(
value = "0x123",
value = "0x1",
type = NetworkAddress.Address.Type.Primary,
),
),
amountToCreateAccount = BigDecimal.ONE,
errorMessage = "",
source = StatusSource.ACTUAL,
source = source,
),
)
}

View file

@ -11,9 +11,9 @@ import com.tangem.data.common.currency.getNetworkDerivationPath
import com.tangem.data.common.currency.getNetworkStandardType
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
/**
[REDACTED_AUTHOR]
@ -47,16 +47,18 @@ class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = default
}
fun createCoin(blockchain: Blockchain): CryptoCurrency {
val derivationPath = getNetworkDerivationPath(
blockchain = blockchain,
extraDerivationPath = null,
cardDerivationStyleProvider = scanResponse.derivationStyleProvider,
)
val network = Network(
id = Network.ID(blockchain.id),
id = Network.ID(blockchain.id, derivationPath),
backendId = blockchain.toNetworkId(),
name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath(
blockchain = blockchain,
extraDerivationPath = null,
cardDerivationStyleProvider = scanResponse.derivationStyleProvider,
),
derivationPath = derivationPath,
currencySymbol = blockchain.currency,
standardType = getNetworkStandardType(blockchain),
hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource,
@ -69,6 +71,12 @@ class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = default
// Impossible to create custom token by CryptoCurrencyFactory because it works with URI under the hood
fun createCustomToken(blockchain: Blockchain, derivationBlockchain: Blockchain): CryptoCurrency {
val derivationPath = Network.DerivationPath.Custom(
value = derivationBlockchain.derivationPath(
scanResponse.derivationStyleProvider.getDerivationStyle(),
)!!.rawPath,
)
return CryptoCurrency.Token(
id = CryptoCurrency.ID(
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
@ -76,15 +84,11 @@ class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = default
suffix = CryptoCurrency.ID.Suffix.RawID(blockchain.id),
),
network = Network(
id = Network.ID(value = blockchain.id),
id = Network.ID(value = blockchain.id, derivationPath),
backendId = "NEVER-MIND",
name = blockchain.fullName,
currencySymbol = "NEVER-MIND",
derivationPath = Network.DerivationPath.Custom(
value = derivationBlockchain.derivationPath(
scanResponse.derivationStyleProvider.getDerivationStyle(),
)!!.rawPath,
),
derivationPath = derivationPath,
isTestnet = false,
standardType = Network.StandardType.ERC20,
hasFiatFeeRate = true,
@ -102,7 +106,13 @@ class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = default
fun createToken(blockchain: Blockchain): CryptoCurrency {
return factory.createToken(
sdkToken = Token(symbol = "NEVER-MIND", contractAddress = "NEVER-MIND", decimals = 8),
sdkToken = Token(
name = "NEVER-MIND",
symbol = "NEVER-MIND",
contractAddress = "NEVER-MIND",
decimals = 8,
id = "NEVER-MIND",
),
blockchain = blockchain,
extraDerivationPath = null,
scanResponse = scanResponse,

View file

@ -0,0 +1,64 @@
package com.tangem.common.test.domain.walletmanager
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.walletmanager.model.Address
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
class MockUpdateWalletManagerResultFactory {
fun createUnreachable(): UpdateWalletManagerResult {
return UpdateWalletManagerResult.Unreachable(selectedAddress = null, addresses = null)
}
fun createUnreachableWithAddress(): UpdateWalletManagerResult {
return UpdateWalletManagerResult.Unreachable(
selectedAddress = "0x1",
addresses = setOf(Address(value = "0x1", type = Address.Type.Primary)),
)
}
fun createNoAccount(): UpdateWalletManagerResult {
return UpdateWalletManagerResult.NoAccount(
selectedAddress = "0x1",
addresses = setOf(Address(value = "0x1", type = Address.Type.Primary)),
amountToCreateAccount = BigDecimal.ONE,
errorMessage = "",
)
}
fun createVerified(): UpdateWalletManagerResult {
return UpdateWalletManagerResult.Verified(
selectedAddress = "0x1",
addresses = setOf(Address(value = "0x1", type = Address.Type.Primary)),
currenciesAmounts = setOf(
CryptoCurrencyAmount.Coin(value = BigDecimal.ONE),
),
currentTransactions = setOf(
CryptoCurrencyTransaction.Coin(txInfo),
),
)
}
private companion object {
val txInfo = TxInfo(
txHash = "erroribus",
timestampInMillis = 2771,
isOutgoing = false,
destinationType = TxInfo.DestinationType.Single(
addressType = TxInfo.AddressType.User(address = "0x1"),
),
sourceType = TxInfo.SourceType.Single(address = "0x2"),
interactionAddressType = null,
status = TxInfo.TransactionStatus.Confirmed,
type = TxInfo.TransactionType.Transfer,
amount = BigDecimal.ONE,
)
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.common.test.utils
import org.junit.jupiter.params.provider.MethodSource
/**
[REDACTED_AUTHOR]
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MethodSource("provideTestModels")
annotation class ProvideTestModels

View file

@ -4,8 +4,8 @@ import com.tangem.common.ui.bottomsheet.receive.AddressModel
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList

View file

@ -5,9 +5,9 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
private const val DSC_ADDRESS_NAME = "DSC"
private const val DEL_ADDRESS_NAME = "Main"
@ -59,7 +59,7 @@ private fun Set<NetworkAddress.Address>.mapToAddressModels(name: TextReference,
}
private fun Network.getAddressDisplayName(addressType: NetworkAddress.Address.Type): TextReference {
return when (id.value) {
return when (rawId) {
"decimal", "decimal/test" -> {
when (addressType) {
NetworkAddress.Address.Type.Primary -> stringReference(value = DEL_ADDRESS_NAME)

View file

@ -6,8 +6,8 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -41,9 +41,11 @@ data class TokenReceiveBottomSheetConfig(
@Immutable
sealed class Asset {
abstract val displaySymbol: TextReference
data class Currency(val name: String, val symbol: String) : Asset() {
override val displaySymbol = stringReference(symbol)
}
data object NFT : Asset() {
override val displaySymbol = resourceReference(R.string.common_nft)
}

View file

@ -4,14 +4,13 @@ import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.tangem.common.ui.notifications.NotificationUM.Warning
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.uncapped
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
@ -460,7 +459,7 @@ object NotificationsFactory {
onCloseClick: (Class<out NotificationUM>) -> Unit,
) {
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
val isTezos = isTezos(cryptoCurrencyStatus.currency.network.id.value)
val isTezos = isTezos(cryptoCurrencyStatus.currency.network.rawId)
val threshold = getTezosThreshold()
val isTotalBalance = enteredAmountValue >= balance && balance > threshold
if (!ignoreAmountReduce && isTotalBalance && isTezos) {

View file

@ -141,7 +141,7 @@ class TokenItemStateConverter(
}
private fun CryptoCurrencyStatus.getStakedBalance() = (value.yieldBalance as? YieldBalance.Data)
?.getTotalWithRewardsStakingBalance(blockchainId = currency.network.id.value).orZero()
?.getTotalWithRewardsStakingBalance(blockchainId = currency.network.rawId).orZero()
private fun createTitleState(currencyStatus: CryptoCurrencyStatus): TokenItemState.TitleState {
return when (val value = currencyStatus.value) {