fix: crash when generating an icon for a P2SH/P2PKH recipient

Confirmed live via logcat: tapping "send" to a P2SH address force-closed
the app - FATAL EXCEPTION: IllegalArgumentException: Bech32 string is
mixed case, thrown from Chain.accountIdOf() -> bitcoinAddressToAccountId()
(native-SegWit-only) via the Confirm Send screen's address icon generator,
a completely separate call path from the transaction-building fix already
shipped. Chain.accountIdOf() now falls back to decodeBitcoinDestination()
for P2SH/P2PKH, safe here because this generic accountId is only ever used
for opaque purposes (identicons, presence checks) - the real send path
already bypasses it entirely and builds the scriptPubKey straight from the
typed BitcoinDestination.
This commit is contained in:
2026-07-13 06:52:34 -07:00
parent 9629e25e69
commit 4d1fb0256f
2 changed files with 21 additions and 1 deletions
@@ -10,6 +10,8 @@ import io.novafoundation.nova.common.utils.TokenSymbol
import io.novafoundation.nova.common.utils.Urls
import io.novafoundation.nova.common.utils.asTokenSymbol
import io.novafoundation.nova.common.utils.bitcoinAddressToAccountId
import io.novafoundation.nova.common.utils.decodeBitcoinDestination
import io.novafoundation.nova.common.utils.hash
import io.novafoundation.nova.common.utils.bitcoinPublicKeyToAccountId
import io.novafoundation.nova.common.utils.emptyBitcoinAccountId
import io.novafoundation.nova.common.utils.emptyEthereumAccountId
@@ -303,7 +305,12 @@ fun ByteArray.toEthereumAddress(): String {
fun Chain.accountIdOf(address: String): ByteArray {
return when {
isTronBased -> address.tronAddressToAccountId()
isBitcoinBased -> address.bitcoinAddressToAccountId()
// Falls back to decodeBitcoinDestination() for a valid P2SH/P2PKH address (real exchange withdrawal
// addresses were confirmed to be P2SH-only) - this wallet's own address stays native-SegWit-only, but a
// SEND destination legitimately isn't. Safe here ONLY because this generic accountId is used for
// opaque purposes (identicon generation, presence checks) elsewhere, never fed back into building a
// scriptPubKey - see BitcoinDestinationAddress.kt's [hash] doc.
isBitcoinBased -> runCatching { address.bitcoinAddressToAccountId() }.getOrElse { address.decodeBitcoinDestination().hash }
isEthereumBased -> address.asEthereumAddress().toAccountId().value
else -> address.toAccountId()
}