Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-27 21:21:17 +04:00
parent 229a2d7236
commit e9fac0e50e
10 changed files with 101 additions and 39 deletions

View file

@ -1,109 +0,0 @@
package com.tangem.tap.domain.card
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.FeePaidCurrency
import com.tangem.blockchain.common.Token
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.data.common.currency.getNetworkDerivationPath
import com.tangem.data.common.currency.getNetworkStandardType
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
/**
[REDACTED_AUTHOR]
*/
internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
private val factory = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains())
val cardano by lazy { listOf(createCoin(blockchain = Blockchain.Cardano)) }
val chia by lazy { listOf(element = createCoin(Blockchain.Chia)) }
val ethereum by lazy { listOf(element = createCoin(Blockchain.Ethereum)) }
val chiaAndEthereum by lazy {
listOf(
createCoin(blockchain = Blockchain.Chia),
createCoin(blockchain = Blockchain.Ethereum),
)
}
val ethereumAndStellar by lazy {
listOf(
createCoin(blockchain = Blockchain.Ethereum),
createCoin(blockchain = Blockchain.Stellar),
)
}
val ethereumTokenWithBinanceDerivation by lazy {
listOf(
createCustomToken(blockchain = Blockchain.Ethereum, derivationBlockchain = Blockchain.Binance),
)
}
private fun createCoin(blockchain: Blockchain): CryptoCurrency {
val network = Network(
id = Network.ID(blockchain.id),
backendId = blockchain.toNetworkId(),
name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath(
blockchain,
extraDerivationPath = null,
scanResponse.derivationStyleProvider,
),
currencySymbol = blockchain.currency,
standardType = getNetworkStandardType(blockchain),
hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource,
canHandleTokens = false,
transactionExtrasType = Network.TransactionExtrasType.NONE,
)
return factory.createCoin(network = network)
}
// Impossible to create custom token by CryptoCurrencyFactory because it works with URI under the hood
private fun createCustomToken(blockchain: Blockchain, derivationBlockchain: Blockchain): CryptoCurrency {
return CryptoCurrency.Token(
id = CryptoCurrency.ID(
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
body = CryptoCurrency.ID.Body.NetworkId(blockchain.id),
suffix = CryptoCurrency.ID.Suffix.RawID(blockchain.id),
),
network = Network(
id = Network.ID(value = blockchain.id),
backendId = "NEVER-MIND",
name = blockchain.fullName,
currencySymbol = "NEVER-MIND",
derivationPath = Network.DerivationPath.Custom(
value = derivationBlockchain.derivationPath(
scanResponse.derivationStyleProvider.getDerivationStyle(),
)!!.rawPath,
),
isTestnet = false,
standardType = Network.StandardType.ERC20,
hasFiatFeeRate = true,
canHandleTokens = true,
transactionExtrasType = Network.TransactionExtrasType.NONE,
),
name = "NEVER-MIND",
symbol = "NEVER-MIND",
decimals = 8,
iconUrl = null,
isCustom = false,
contractAddress = "NEVER-MIND",
)
}
private fun createToken(blockchain: Blockchain): CryptoCurrency {
return factory.createToken(
sdkToken = Token(symbol = "NEVER-MIND", contractAddress = "NEVER-MIND", decimals = 8),
blockchain = blockchain,
extraDerivationPath = null,
scanResponse = scanResponse,
)!!
}
}

View file

@ -4,6 +4,8 @@ import android.annotation.SuppressLint
import com.google.common.truth.Truth
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.CompletionResult
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.common.configs.GenericCardConfig
@ -40,7 +42,7 @@ internal class DefaultDerivationsRepositoryTest {
artworkUrl = "",
cardsInWallet = setOf(),
isMultiCurrency = false,
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
hasBackupError = false,
)
@ -77,7 +79,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success if currencies is empty`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
@ -94,7 +96,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success if card already has derivations`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(
scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
),
@ -105,7 +107,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { Truth.assertThat(it) }
@ -119,7 +121,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `error if tangemSdkManager throws exception`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled
@ -127,7 +129,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { error("Should throws exception") }
@ -142,7 +144,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success case`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success(
@ -153,7 +155,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { Truth.assertThat(it) }

View file

@ -6,6 +6,8 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationConfigV2
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.common.configs.MultiWalletCardConfig
@ -20,7 +22,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for empty currencies`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val actual = finder.find(emptyList())
@ -31,10 +33,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for non supported blockchains`() {
// Bls is not supported
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chia
val currencies = MockCryptoCurrencyFactory(scanResponse).chia.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty()
@ -43,7 +45,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations ONLY for supported blockchains`() {
// Bls is not supported
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(2),
derivedKeys = emptyMap(),
).let {
@ -55,7 +57,7 @@ internal class MissedDerivationsFinderTest {
}
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chiaAndEthereum
val currencies = MockCryptoCurrencyFactory(scanResponse).chiaAndEthereum
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -66,10 +68,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations for custom token`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumTokenWithBinanceDerivation
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumTokenWithBinanceDerivation
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -83,10 +85,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations for cardano`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).cardano
val currencies = MockCryptoCurrencyFactory(scanResponse).cardano.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -105,13 +107,13 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for already derived currencies`() {
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = Wallet2CardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
)
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereum
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereum.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty()
@ -119,13 +121,13 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations ONLY for never derived currencies`() {
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
)
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumAndStellar
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumAndStellar
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(

View file

@ -1,107 +0,0 @@
package com.tangem.tap.domain.card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.configs.*
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.attestation.Attestation
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import java.util.Date
/**
[REDACTED_AUTHOR]
*/
object ScanResponseMockFactory {
private val genericFirmwareVersion = CardDTO.FirmwareVersion(
major = 3,
minor = 29,
patch = 0,
type = FirmwareVersion.FirmwareType.Release,
)
private val walletFirmwareVersion = CardDTO.FirmwareVersion(
major = 4,
minor = 0,
patch = 0,
type = FirmwareVersion.FirmwareType.Release,
)
private val wallet2FirmwareVersion = CardDTO.FirmwareVersion(
major = 6,
minor = 33,
patch = 0,
type = FirmwareVersion.FirmwareType.Release,
)
fun create(cardConfig: CardConfig, derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap>): ScanResponse {
return ScanResponse(
card = CardDTO(
cardId = "NEVER-MIND",
batchId = "NEVER-MIND",
cardPublicKey = ByteArray(0),
firmwareVersion = when (cardConfig) {
is GenericCardConfig -> genericFirmwareVersion
MultiWalletCardConfig -> walletFirmwareVersion
Wallet2CardConfig -> wallet2FirmwareVersion
EdSingleCurrencyCardConfig -> genericFirmwareVersion
},
manufacturer = CardDTO.Manufacturer(name = "NEVER-MIND", manufactureDate = Date(), signature = null),
issuer = CardDTO.Issuer(name = "NEVER-MIND", publicKey = ByteArray(0)),
settings = CardDTO.Settings(
securityDelay = 0,
maxWalletsCount = 0,
isSettingAccessCodeAllowed = false,
isSettingPasscodeAllowed = false,
isResettingUserCodesAllowed = false,
isLinkedTerminalEnabled = false,
isBackupAllowed = cardConfig is MultiWalletCardConfig,
supportedEncryptionModes = listOf(),
isFilesAllowed = true,
isHDWalletAllowed = cardConfig is MultiWalletCardConfig,
isKeysImportAllowed = true,
),
userSettings = null,
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.Current,
isAccessCodeSet = false,
isPasscodeSet = null,
supportedCurves = listOf(),
wallets = cardConfig.mandatoryCurves.map {
CardDTO.Wallet(
CardWallet(
publicKey = it.name.toByteArray(), // IMPORTANT: public key must equal to curve name
chainCode = null,
curve = it,
settings = createSettings(),
totalSignedHashes = null,
remainingSignatures = null,
index = 0,
isImported = true,
hasBackup = true,
derivedKeys = mapOf(),
),
)
},
attestation = Attestation(
cardKeyAttestation = Attestation.Status.Skipped,
walletKeysAttestation = Attestation.Status.Skipped,
firmwareAttestation = Attestation.Status.Skipped,
cardUniquenessAttestation = Attestation.Status.Skipped,
),
backupStatus = null,
),
productType = ProductType.Wallet2,
walletData = null,
derivedKeys = derivedKeys,
)
}
private fun createSettings(): CardWallet.Settings {
val constructor = CardWallet.Settings::class.java.declaredConstructors[0]
constructor.isAccessible = true
return constructor.newInstance(false) as CardWallet.Settings
}
}

View file

@ -1,10 +1,10 @@
package com.tangem.tap.domain.userWalletList.implementation
import com.google.common.truth.Truth
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.card.ScanResponseMockFactory
import io.mockk.mockk
import org.junit.Test
import org.junit.runner.RunWith
@ -198,7 +198,7 @@ internal class BiometricUserWalletsListManagerTest(private val model: Model) {
cardsInWallet = emptySet(),
isMultiCurrency = true,
hasBackupError = false,
scanResponse = ScanResponseMockFactory.create(
scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(maxWalletCount = 1),
derivedKeys = emptyMap(),
).let {