Updated on 2026-08-14
This commit is contained in:
parent
72989eab5a
commit
361bd8168b
4 changed files with 74 additions and 3 deletions
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.toBlockchain
|
|||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.KeyWalletPublicKey
|
||||
|
|
@ -140,11 +141,12 @@ class MissedDerivationsFinder private constructor(
|
|||
* - Account-level (e.g. m/84'/0'/0') — the XPUB itself
|
||||
* - Parent (e.g. m/84'/0') — needed for parent fingerprint in XPUB serialization
|
||||
*
|
||||
* Only applicable for BIP44-style XPUB blockchains (BTC, BCH, LTC, DOGE, DASH, RVN).
|
||||
* Only applicable for blockchains listed in [DynamicAddressesSupportedBlockchains]
|
||||
* (BTC/LTC via BIP-84 SegWit, BCH/DOGE/DASH/RVN via BIP-44, plus their testnets).
|
||||
*/
|
||||
private fun Blockchain.getXpubDerivationPaths(derivationPath: DerivationPath): List<DerivationPath> {
|
||||
if (!isDynamicAddressesEnabled) return emptyList()
|
||||
if (!isBip44DerivationStyleXPUB()) return emptyList()
|
||||
if (!DynamicAddressesSupportedBlockchains.isSupported(this)) return emptyList()
|
||||
|
||||
val nodes = derivationPath.nodes
|
||||
if (nodes.size < XPUB_MIN_NODES) return emptyList()
|
||||
|
|
|
|||
|
|
@ -132,6 +132,73 @@ internal class MissedDerivationsFinderTest {
|
|||
Truth.assertThat(actual).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `XPUB derivations added for supported blockchain when dynamic addresses enabled`() {
|
||||
val userWallet = MockUserWalletFactory.create(createWallet2ScanResponse())
|
||||
val finder = MissedDerivationsFinder(userWallet = userWallet, isDynamicAddressesEnabled = true)
|
||||
|
||||
val currencies = listOf(MockCryptoCurrencyFactory(userWallet).createCoin(Blockchain.Bitcoin))
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
ByteArrayKey(EllipticCurve.Secp256k1.name.toByteArray()),
|
||||
listOf(
|
||||
DerivationPath("m/84'/0'/0'/0/0"), // Bitcoin BIP-84 default
|
||||
DerivationPath("m/84'/0'/0'"), // XPUB account-level path
|
||||
DerivationPath("m/84'/0'"), // Parent (for XPUB fingerprint)
|
||||
DerivationPath("m/44'/60'/0'/0/0"), // Ethereum added by enrichBlockchains
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `XPUB derivations NOT added for supported blockchain when dynamic addresses disabled`() {
|
||||
val userWallet = MockUserWalletFactory.create(createWallet2ScanResponse())
|
||||
val finder = MissedDerivationsFinder(userWallet = userWallet, isDynamicAddressesEnabled = false)
|
||||
|
||||
val currencies = listOf(MockCryptoCurrencyFactory(userWallet).createCoin(Blockchain.Bitcoin))
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
ByteArrayKey(EllipticCurve.Secp256k1.name.toByteArray()),
|
||||
listOf(
|
||||
DerivationPath("m/84'/0'/0'/0/0"),
|
||||
DerivationPath("m/44'/60'/0'/0/0"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `XPUB derivations NOT added for unsupported blockchain when dynamic addresses enabled`() {
|
||||
val userWallet = MockUserWalletFactory.create(createWallet2ScanResponse())
|
||||
val finder = MissedDerivationsFinder(userWallet = userWallet, isDynamicAddressesEnabled = true)
|
||||
|
||||
val currencies = listOf(MockCryptoCurrencyFactory(userWallet).createCoin(Blockchain.Ethereum))
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
ByteArrayKey(EllipticCurve.Secp256k1.name.toByteArray()),
|
||||
listOf(DerivationPath("m/44'/60'/0'/0/0")),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wallet2 config yields DerivationStyle.V3 (BIP-84 SegWit for BTC/LTC) — the style the
|
||||
* Dynamic Addresses feature actually targets. [MockScanResponseFactory] hardcodes
|
||||
* `isHDWalletAllowed = false` for Wallet2, so we patch it to `true` to mirror production
|
||||
* scans that reach [MissedDerivationsFinder].
|
||||
*/
|
||||
private fun createWallet2ScanResponse() = MockScanResponseFactory.create(
|
||||
cardConfig = Wallet2CardConfig,
|
||||
derivedKeys = emptyMap(),
|
||||
).let {
|
||||
it.copy(
|
||||
card = it.card.copy(
|
||||
settings = it.card.settings.copy(isHDWalletAllowed = true, isBackupAllowed = true),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `derivations ONLY for never derived currencies`() {
|
||||
val scanResponse = MockScanResponseFactory.create(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue