diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 4bf9538ce4..f4809d2272 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -2377,6 +2377,7 @@ All dApps disconnected Allow to spend By approving, you allow dApp or Smart contract to use tokens in future transactions. + Change address Address Connect Loading diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/bitcoin/WcBitcoinSignPsbtUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/bitcoin/WcBitcoinSignPsbtUseCase.kt index 302376444a..af1d2ac464 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/bitcoin/WcBitcoinSignPsbtUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/bitcoin/WcBitcoinSignPsbtUseCase.kt @@ -19,7 +19,9 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.walletconnect.WcTransactionSignerProvider import com.tangem.domain.walletconnect.model.HandleMethodError import com.tangem.domain.walletconnect.model.WcBitcoinMethod +import com.tangem.domain.walletconnect.model.WcPsbtOutput import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck +import com.tangem.domain.walletconnect.usecase.method.WcPsbtUseCase import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase import com.tangem.domain.walletmanager.WalletManagersFacade @@ -51,7 +53,8 @@ internal class WcBitcoinSignPsbtUseCase @AssistedInject constructor( blockAidDelegate: BlockAidVerificationDelegate, @SdkMoshi private val moshi: Moshi, ) : BaseWcSignUseCase(), - WcTransactionUseCase { + WcTransactionUseCase, + WcPsbtUseCase { override val wallet get() = context.session.wallet @@ -120,6 +123,20 @@ internal class WcBitcoinSignPsbtUseCase @AssistedInject constructor( } } + override suspend fun parsePsbtOutputs(): List { + val walletManager = walletManagersFacade.getOrCreateWalletManager(wallet.walletId, network) + ?: return emptyList() + return when (val result = walletManager.parsePsbtOutputs(method.psbt)) { + is SdkResult.Success -> result.data.map { output -> + WcPsbtOutput( + address = output.address, + amountSatoshi = output.amountSatoshi, + ) + } + is SdkResult.Failure -> emptyList() + } + } + override fun invoke(): Flow> { val transactionData = TransactionData.Compiled( value = TransactionData.Compiled.Data.RawString(method.psbt), diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/BlockAidVerificationDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/BlockAidVerificationDelegate.kt index bcd5f64cb5..d18221f88b 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/BlockAidVerificationDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/utils/BlockAidVerificationDelegate.kt @@ -54,13 +54,17 @@ internal class BlockAidVerificationDelegate @Inject constructor( is WcSolanaMethod.SignAllTransaction -> TransactionParams.Solana(method.transaction) is WcSolanaMethod.SignTransaction -> TransactionParams.Solana(listOf(method.transaction)) is WcSolanaMethod.SignAndSendTransaction -> TransactionParams.Solana(listOf(method.transaction)) - is WcSolanaMethod.SignMessage, - is WcBitcoinMethod, - -> { - // BlockAid doesn't support Solana message signing and Bitcoin methods + is WcSolanaMethod.SignMessage -> { + // BlockAid doesn't support Solana message signing emit(Lce.Content(createSafeResult())) return@flow } + is WcBitcoinMethod -> { + // BlockAid doesn't support Bitcoin methods: don't synthesize a SAFE result for an unscanned + // transaction + emit(Lce.Content(failedResult)) + return@flow + } else -> { emit(Lce.Content(failedResult)) return@flow diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPsbtOutput.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPsbtOutput.kt new file mode 100644 index 0000000000..0c960e9523 --- /dev/null +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPsbtOutput.kt @@ -0,0 +1,13 @@ +package com.tangem.domain.walletconnect.model + +/** + * A single output of a parsed PSBT (Partially Signed Bitcoin Transaction). + * + * @property address recipient address decoded from the output script, or `null` if it could not be decoded + * (e.g. `OP_RETURN` or non-standard scripts) + * @property amountSatoshi output amount, in satoshi + */ +data class WcPsbtOutput( + val address: String?, + val amountSatoshi: Long, +) \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcPsbtUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcPsbtUseCase.kt new file mode 100644 index 0000000000..5c0824ca3a --- /dev/null +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/method/WcPsbtUseCase.kt @@ -0,0 +1,17 @@ +package com.tangem.domain.walletconnect.usecase.method + +import com.tangem.domain.walletconnect.model.WcPsbtOutput + +/** + * WalletConnect use case that can expose the outputs of a PSBT (`signPsbt` method) for display before signing. + * + */ +interface WcPsbtUseCase { + + /** + * Parses the PSBT of the current request and returns its outputs (recipient + amount). + * + * Returns an empty list if the PSBT cannot be parsed (the UI then falls back to the raw request data). + */ + suspend fun parsePsbtOutputs(): List +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverter.kt new file mode 100644 index 0000000000..570f411303 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverter.kt @@ -0,0 +1,52 @@ +package com.tangem.features.walletconnect.transaction.converter + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.walletconnect.model.WcBitcoinMethod +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoItemUM +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toImmutableList +import javax.inject.Inject + +/** + * Builds a transaction request details block (recipient and amount) for a Bitcoin `sendTransfer` WalletConnect request. + * + */ +internal class WcBtcSendTransferRequestInfoConverter @Inject constructor() : + Converter { + + override fun convert(value: Input): WcTransactionRequestBlockUM { + val method = value.method + return WcTransactionRequestBlockUM( + info = buildList { + add(WcTransactionRequestInfoItemUM(resourceReference(R.string.common_from), method.account)) + add(WcTransactionRequestInfoItemUM(resourceReference(R.string.common_to), method.recipientAddress)) + add( + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.common_amount), + description = formatAmount(method.amount, value.decimals, value.symbol), + ), + ) + val changeAddress = method.changeAddress + if (!changeAddress.isNullOrEmpty()) { + add(WcTransactionRequestInfoItemUM(resourceReference(R.string.wc_change_address), changeAddress)) + } + }.toImmutableList(), + ) + } + + private fun formatAmount(amount: String, decimals: Int, symbol: String): String { + val value = amount.toBigDecimalOrNull() + ?.movePointLeft(decimals) + ?.stripTrailingZeros() + ?: return amount + return "${value.toPlainString()} $symbol" + } + + data class Input( + val method: WcBitcoinMethod.SendTransfer, + val decimals: Int, + val symbol: String, + ) +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt index b4f72c25b2..6decf0a891 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt @@ -6,6 +6,7 @@ import com.tangem.domain.models.wallet.isHotWallet import com.tangem.domain.walletconnect.model.WcBitcoinMethod import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.model.WcMethod +import com.tangem.domain.walletconnect.model.WcPsbtOutput import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck import com.tangem.domain.walletconnect.usecase.method.WcMethodContext @@ -15,11 +16,13 @@ import com.tangem.features.send.api.entity.FeeSelectorUM import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionFeeState +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionItemUM import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM import com.tangem.features.walletconnect.utils.WcNotificationsFactory import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject @@ -27,6 +30,8 @@ internal class WcSendTransactionUMConverter @Inject constructor( private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter, private val networkInfoUMConverter: WcNetworkInfoUMConverter, private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter, + private val btcSendTransferRequestInfoConverter: WcBtcSendTransferRequestInfoConverter, + private val signPsbtRequestInfoConverter: WcSignPsbtRequestInfoConverter, private val notificationsFactory: WcNotificationsFactory, ) : Converter { @@ -76,13 +81,7 @@ internal class WcSendTransactionUMConverter @Inject constructor( is WcTransactionFeeState.Success -> value.feeSelectorUM ?: FeeSelectorUM.Loading }, transactionRequestInfo = WcTransactionRequestInfoUM( - blocks = buildList { - addAll( - requestBlockUMConverter.convert( - WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest), - ), - ) - }.toImmutableList(), + blocks = buildRequestInfoBlocks(value), onCopy = value.actions.onCopy, ), ) @@ -97,6 +96,37 @@ internal class WcSendTransactionUMConverter @Inject constructor( } } + private fun buildRequestInfoBlocks(value: Input): ImmutableList = buildList { + addAll( + requestBlockUMConverter.convert( + WcTransactionRequestBlockUMConverter.Input(value.context.rawSdkRequest), + ), + ) + val method = value.context.method + if (method is WcBitcoinMethod.SendTransfer) { + add( + btcSendTransferRequestInfoConverter.convert( + WcBtcSendTransferRequestInfoConverter.Input( + method = method, + decimals = value.cryptoCurrencyStatus.currency.decimals, + symbol = value.cryptoCurrencyStatus.currency.symbol, + ), + ), + ) + } + if (method is WcBitcoinMethod.SignPsbt && value.psbtOutputs != null) { + addAll( + signPsbtRequestInfoConverter.convert( + WcSignPsbtRequestInfoConverter.Input( + outputs = value.psbtOutputs, + decimals = value.cryptoCurrencyStatus.currency.decimals, + symbol = value.cryptoCurrencyStatus.currency.symbol, + ), + ), + ) + } + }.toImmutableList() + data class Input( val context: WcMethodContext, val portfolioName: AccountTitleUM?, @@ -106,6 +136,7 @@ internal class WcSendTransactionUMConverter @Inject constructor( val feeSelectorUM: FeeSelectorUM?, val cryptoCurrencyStatus: CryptoCurrencyStatus, val securityCheck: BlockAidTransactionCheck.Result?, + val psbtOutputs: List?, val onFeeReload: () -> Unit, ) } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverter.kt new file mode 100644 index 0000000000..0d6c17811f --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverter.kt @@ -0,0 +1,66 @@ +package com.tangem.features.walletconnect.transaction.converter + +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.walletconnect.model.WcPsbtOutput +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoItemUM +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toImmutableList +import java.math.BigDecimal +import javax.inject.Inject + +/** + * Builds transaction request details blocks (recipient + amount per output) for a Bitcoin `signPsbt` request. + * + */ +internal class WcSignPsbtRequestInfoConverter @Inject constructor() : + Converter> { + + override fun convert(value: Input): List { + val outputs = value.outputs + if (outputs.isEmpty()) return emptyList() + val shouldShowIndex = outputs.size > 1 + return outputs.mapIndexed { index, output -> + val recipientTitle = when { + output.address == null -> resourceReference(R.string.common_no_address) + shouldShowIndex -> combinedReference( + resourceReference(R.string.common_to), + stringReference(" ${index + 1}"), + ) + else -> resourceReference(R.string.common_to) + } + WcTransactionRequestBlockUM( + info = buildList { + add( + WcTransactionRequestInfoItemUM( + title = recipientTitle, + description = output.address.orEmpty(), + ), + ) + add( + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.common_amount), + description = formatAmount(output.amountSatoshi, value.decimals, value.symbol), + ), + ) + }.toImmutableList(), + ) + } + } + + private fun formatAmount(amountSatoshi: Long, decimals: Int, symbol: String): String { + val value = BigDecimal.valueOf(amountSatoshi) + .movePointLeft(decimals) + .stripTrailingZeros() + return "${value.toPlainString()} $symbol" + } + + data class Input( + val outputs: List, + val decimals: Int, + val symbol: String, + ) +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 171810fbb8..b03a710fd8 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -38,6 +38,7 @@ import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents.SignatureRequestReceived.EmulationStatus import com.tangem.domain.walletconnect.WcAnalyticEvents.SolanaLargeTransaction import com.tangem.domain.walletconnect.WcRequestUseCaseFactory +import com.tangem.domain.walletconnect.model.WcPsbtOutput import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.WcRequestError.Companion.message import com.tangem.domain.walletconnect.usecase.method.* @@ -100,6 +101,7 @@ internal class WcSendTransactionModel @Inject constructor( private var useCase: WcSignUseCase<*> by Delegates.notNull() private var signState: WcSignState<*> by Delegates.notNull() private var wcApproval: WcApproval? = null + private var psbtOutputs: List? = null private var sign: () -> Unit = {} private val blockAidUiConverter = WcSendAndReceiveBlockAidUiConverter() private val feeReloadState = MutableStateFlow(false) @@ -126,6 +128,9 @@ internal class WcSendTransactionModel @Inject constructor( .onNone { unknownMethodRunnable() } .getOrNull() ?: return@launch this@WcSendTransactionModel.useCase = useCase + if (useCase is WcPsbtUseCase) { + psbtOutputs = useCase.parsePsbtOutputs() + } (useCase as? WcMutableFee) ?.dAppFee() ?.let { dAppFee -> @@ -283,6 +288,7 @@ internal class WcSendTransactionModel @Inject constructor( cryptoCurrencyStatus = cryptoCurrencyStatus, onFeeReload = ::triggerFeeReload, securityCheck = securityCheck.getOrNull(), + psbtOutputs = psbtOutputs, portfolioName = portfolioNameDelegate.createAccountTitleUM(useCase.session), ), ) diff --git a/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverterTest.kt b/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverterTest.kt new file mode 100644 index 0000000000..30bd3a50bf --- /dev/null +++ b/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcBtcSendTransferRequestInfoConverterTest.kt @@ -0,0 +1,106 @@ +package com.tangem.features.walletconnect.transaction.converter + +import com.google.common.truth.Truth +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.walletconnect.model.WcBitcoinMethod +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM +import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoItemUM +import kotlinx.collections.immutable.toImmutableList +import org.junit.jupiter.api.Test + +class WcBtcSendTransferRequestInfoConverterTest { + + private val converter = WcBtcSendTransferRequestInfoConverter() + + @Test + fun `GIVEN sendTransfer with change address WHEN convert THEN block with from to amount and change`() { + val input = WcBtcSendTransferRequestInfoConverter.Input( + method = WcBitcoinMethod.SendTransfer( + account = "bc1qsenderaddress", + recipientAddress = "bc1qrecipientaddress", + amount = "5000000", + memo = null, + changeAddress = "bc1qchangeaddress", + ), + decimals = 8, + symbol = "BTC", + ) + + val expected = WcTransactionRequestBlockUM( + info = listOf( + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_from), "bc1qsenderaddress"), + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_to), "bc1qrecipientaddress"), + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_amount), "0.05 BTC"), + WcTransactionRequestInfoItemUM(resourceReference(R.string.wc_change_address), "bc1qchangeaddress"), + ).toImmutableList(), + ) + + Truth.assertThat(converter.convert(input)).isEqualTo(expected) + } + + @Test + fun `GIVEN sendTransfer without change address WHEN convert THEN block with from to amount only`() { + val input = WcBtcSendTransferRequestInfoConverter.Input( + method = WcBitcoinMethod.SendTransfer( + account = "bc1qsenderaddress", + recipientAddress = "bc1qrecipientaddress", + amount = "100000000", + memo = null, + changeAddress = null, + ), + decimals = 8, + symbol = "BTC", + ) + + val expected = WcTransactionRequestBlockUM( + info = listOf( + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_from), "bc1qsenderaddress"), + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_to), "bc1qrecipientaddress"), + WcTransactionRequestInfoItemUM(resourceReference(R.string.common_amount), "1 BTC"), + ).toImmutableList(), + ) + + Truth.assertThat(converter.convert(input)).isEqualTo(expected) + } + + @Test + fun `GIVEN blank change address WHEN convert THEN change address item is omitted`() { + val input = WcBtcSendTransferRequestInfoConverter.Input( + method = WcBitcoinMethod.SendTransfer( + account = "bc1qsenderaddress", + recipientAddress = "bc1qrecipientaddress", + amount = "1", + memo = null, + changeAddress = "", + ), + decimals = 8, + symbol = "BTC", + ) + + val result = converter.convert(input) + + Truth.assertThat(result.info).hasSize(3) + Truth.assertThat(result.info.last().title).isEqualTo(resourceReference(R.string.common_amount)) + } + + @Test + fun `GIVEN non-numeric amount WHEN convert THEN raw amount is shown`() { + val input = WcBtcSendTransferRequestInfoConverter.Input( + method = WcBitcoinMethod.SendTransfer( + account = "bc1qsenderaddress", + recipientAddress = "bc1qrecipientaddress", + amount = "not-a-number", + memo = null, + changeAddress = null, + ), + decimals = 8, + symbol = "BTC", + ) + + val amountItem = converter.convert(input).info[2] + + Truth.assertThat(amountItem.title).isEqualTo(resourceReference(R.string.common_amount)) + Truth.assertThat(amountItem.description).isEqualTo("not-a-number") + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverterTest.kt b/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverterTest.kt new file mode 100644 index 0000000000..79224f7acd --- /dev/null +++ b/features/walletconnect/impl/src/test/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignPsbtRequestInfoConverterTest.kt @@ -0,0 +1,82 @@ +package com.tangem.features.walletconnect.transaction.converter + +import com.google.common.truth.Truth +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.walletconnect.model.WcPsbtOutput +import com.tangem.features.walletconnect.impl.R +import org.junit.jupiter.api.Test + +class WcSignPsbtRequestInfoConverterTest { + + private val converter = WcSignPsbtRequestInfoConverter() + + @Test + fun `GIVEN single output WHEN convert THEN one block with to and amount`() { + val input = WcSignPsbtRequestInfoConverter.Input( + outputs = listOf(WcPsbtOutput(address = "bc1qrecipient", amountSatoshi = 100_000L)), + decimals = 8, + symbol = "BTC", + ) + + val result = converter.convert(input) + + Truth.assertThat(result).hasSize(1) + Truth.assertThat(result[0].info.map { it.title }).containsExactly( + resourceReference(R.string.common_to), + resourceReference(R.string.common_amount), + ).inOrder() + Truth.assertThat(result[0].info[0].description).isEqualTo("bc1qrecipient") + Truth.assertThat(result[0].info[1].description).isEqualTo("0.001 BTC") + } + + @Test + fun `GIVEN multiple outputs WHEN convert THEN indexed blocks per output`() { + val input = WcSignPsbtRequestInfoConverter.Input( + outputs = listOf( + WcPsbtOutput(address = "bc1qfirst", amountSatoshi = 250_000L), + WcPsbtOutput(address = "bc1qsecond", amountSatoshi = 1_0000_0000L), + ), + decimals = 8, + symbol = "BTC", + ) + + val result = converter.convert(input) + + Truth.assertThat(result).hasSize(2) + Truth.assertThat(result[0].info[0].title) + .isEqualTo(combinedReference(resourceReference(R.string.common_to), stringReference(" 1"))) + Truth.assertThat(result[0].info[1].description).isEqualTo("0.0025 BTC") + Truth.assertThat(result[1].info[0].title) + .isEqualTo(combinedReference(resourceReference(R.string.common_to), stringReference(" 2"))) + Truth.assertThat(result[1].info[0].description).isEqualTo("bc1qsecond") + Truth.assertThat(result[1].info[1].description).isEqualTo("1 BTC") + } + + @Test + fun `GIVEN output with undecodable address WHEN convert THEN no address title and empty recipient`() { + val input = WcSignPsbtRequestInfoConverter.Input( + outputs = listOf(WcPsbtOutput(address = null, amountSatoshi = 50_000L)), + decimals = 8, + symbol = "BTC", + ) + + val result = converter.convert(input) + + Truth.assertThat(result[0].info[0].title).isEqualTo(resourceReference(R.string.common_no_address)) + Truth.assertThat(result[0].info[0].description).isEqualTo("") + Truth.assertThat(result[0].info[1].description).isEqualTo("0.0005 BTC") + } + + @Test + fun `GIVEN no outputs WHEN convert THEN empty list`() { + val input = WcSignPsbtRequestInfoConverter.Input( + outputs = emptyList(), + decimals = 8, + symbol = "BTC", + ) + + Truth.assertThat(converter.convert(input)).isEmpty() + } +} \ No newline at end of file diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index eef36fbda4..074e02bb80 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1566" +tangemBlockchainSdk = "develop-1567" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-624" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^