What changed, and why it matters
This commit adds a new user-interface feature for Bitcoin transactions on the Keystone 3 hardware wallet. When a transaction input only provides a 'witness UTXO' (a lightweight proof of the coin being spent), the device now shows a warning asking the user to verify the input value, and offers a QR code that links to the transaction on a public blockchain explorer (mempool.space). This is a defensive UX improvement, not an attack or vulnerability fix. It helps users spot potentially fraudulent input amounts but does not change the underlying signing logic.
No urgent action required. Treat as a normal feature review. Verify that the 128-byte url buffer is sufficient for the mempool.space URL plus txid (https://mempool.space/tx/ + 64 hex chars + null fits with margin), and that input_txid is always a valid 64-character hex txid before formatting. Consider whether fetching or displaying an external explorer URL could be used in a social-engineering attack, and ensure the QR code cannot be triggered by untrusted data during signing.
Security signals we found
New user-facing warning for witness-only UTXO inputs
New per-input txid/vout exposure through FFI structs
UI now links to external mempool.space URL via QR code
No signing or cryptographic validation logic changed
No buffer-size validation visible around 128-byte url and 160-byte outpoint snprintf_s calls
Evidence from the diff
The change threads two new fields through the Rust transaction parser and C FFI: input_txid/input_vout for every parsed input, and has_witness_only_inputs for the transaction overview. For PSBTs, has_witness_only_inputs is set when any PSBT input has witness_utxo present but non_witness_utxo absent. The C UI then displays a notice card (‘Please confirm each input value is correct.’) and, per input, renders an ‘Input Ref’ link that opens a QR hint box containing a mempool.space URL and the outpoint string. Legacy transaction parsing sets has_witness_only_inputs to false. The simulator QR-from-screen macro is also commented out, which appears unrelated to the security behavior.
Changed components
rust/apps/bitcoin transaction parser (legacy and PSBT)rust/rust_c Bitcoin FFI structssrc/ui/gui_chain/gui_btc.c Bitcoin transaction UIsrc/ui/gui_components/gui_qr_hintbox QR hint box widgetui_simulator simulator_model.cInspect captured patch +167 / −19
diff --git a/rust/apps/bitcoin/src/lib.rs b/rust/apps/bitcoin/src/lib.rs
index 9c9cf1e..c0588ac 100644
--- a/rust/apps/bitcoin/src/lib.rs
+++ b/rust/apps/bitcoin/src/lib.rs
@@ -171,16 +171,19 @@ mod test {
sign_status: Some("Unsigned".to_string()),
is_multisig: false,
need_sign: true,
+ has_witness_only_inputs: false,
}
};
}
macro_rules! build_parsed_input {
- ($address:expr, $amount:expr, $value:expr, $path:expr) => {
+ ($address:expr, $amount:expr, $value:expr, $path:expr, $txid:expr, $vout:expr) => {
ParsedInput {
address: Some($address.to_string()),
amount: $amount.to_string(),
value: $value,
+ input_txid: $txid.to_string(),
+ input_vout: $vout,
path: Some($path.to_string()),
is_multisig: false,
is_external: false,
@@ -209,7 +212,7 @@ mod test {
DetailTx {
from: $from
.iter()
- .map(|i| build_parsed_input!(i.0, i.1, i.2, i.3))
+ .map(|i| build_parsed_input!(i.0, i.1, i.2, i.3, i.4, i.5))
.collect(),
to: $to
.iter()
@@ -249,6 +252,7 @@ mod test {
let payload = prepare_payload(hex);
let context = prepare_parse_context(pubkey_str);
let mut parsed_tx = parse_raw_tx(payload, context).unwrap();
+ assert!(!parsed_tx.overview.has_witness_only_inputs);
// set output is_external always false to pass the test
parsed_tx
.detail
@@ -273,6 +277,8 @@ mod test {
"0.1851975 LTC",
18519750,
"m/49'/2'/0'/0/0",
+ "a59bcbaaae11ba5938434e2d4348243e5e392551156c4a3e88e7bdc0b2a8f663",
+ 1,
);
let output1 = (
"MG67WAWZ6jEmA97LWuERJTkBB7pHMoVfgj",
@@ -334,6 +340,8 @@ mod test {
"0.01 DASH",
1000000,
"m/44'/5'/0'/0/0",
+ "0e1c934b781dcd5217a909343abe4f4d2a1b3904224090ebe83fea66f3a14c9d",
+ 0,
);
let output1 = (
"Xb9LAffWjcxTCN5GMj5kbZiqN5g7nnkgrv",
@@ -394,6 +402,8 @@ mod test {
"0.005555 BCH",
555500,
"m/44'/145'/0'/0/1",
+ "0752bd85eb5d52aba81c2f377930eb60b851fe6fb4b0c9509b4cd810e7c59b91",
+ 0,
);
let output1 = (
"qpt4cr4juduwl36w35rwfwvz6am2z7mxcg8a84k28n",
@@ -455,6 +465,8 @@ mod test {
"0.00298739 BTC",
298739,
"M/84'/0'/0'/1/32",
+ "6ca0ec211a165a8d86b1708a1eb576121efca6b95993afd303400b300688d322",
+ 1,
);
let output1 = (
"bc1qksq4ax9jpqqmumwfhg54ktwh29627zf78237wp",
@@ -516,12 +528,16 @@ mod test {
"0.0000055 BTC",
550,
"M/49'/0'/0'/0/0",
+ "6757786cb8e64f42cf3e5cb86949468426f803c61beee31035989259b9910ba8",
+ 0,
);
let input2 = (
"3G8X84wtGDtiENAYagDSBcofp7NkjqHJFS",
"0.00001 BTC",
1000,
"M/49'/0'/0'/0/1",
+ "43e0fbd9aedddb6cce3a04081209e136eb7bb56622b2a10d93170ce8b720096a",
+ 0,
);
let output1 = (
"3NNSqAz3LajNv6eSQtn237CtPaHdJdYPVR",
@@ -577,12 +593,16 @@ mod test {
"0.00003 BTC",
3000,
"M/44'/0'/0'/0/4",
+ "10e0846616f20e8dee8d0984fc7b8b629794e1ba8494b5e4e7b0a9df54fffdbf",
+ 0,
);
let input2 = (
"1NVWpSCxyzpPgSeGRs4zqFciZ7N1UEQtEc",
"0.000023 BTC",
2300,
"M/44'/0'/0'/0/0",
+ "9bad05221ce0c07fdf7a48bbb7fc7bbd40770748a36ea74fd85eab82e6c5d128",
+ 0,
);
let output1 = (
"bc1qksq4ax9jpqqmumwfhg54ktwh29627zf78237wp",
diff --git a/rust/apps/bitcoin/src/transactions/legacy/parser.rs b/rust/apps/bitcoin/src/transactions/legacy/parser.rs
index 4aa4dd3..85b494e 100644
--- a/rust/apps/bitcoin/src/transactions/legacy/parser.rs
+++ b/rust/apps/bitcoin/src/transactions/legacy/parser.rs
@@ -24,7 +24,7 @@ impl TxParser for TxData {
.map(|each| self.parse_raw_tx_output(each))
.collect();
let network = Network::from_str(&self.network)?;
- self.normalize(mapped_inputs?, mapped_outputs?, &network)
+ self.normalize(mapped_inputs?, mapped_outputs?, &network, false)
}
fn determine_network(&self) -> Result<Network> {
Network::from_str(&self.network)
@@ -41,6 +41,8 @@ impl TxData {
address,
amount: Self::format_amount(input.value, &Network::from_str(network)?),
value: input.value,
+ input_txid: input.previous_output.clone(),
+ input_vout: input.vout,
path: Some(path.to_string()),
is_multisig: false,
is_external: false,
diff --git a/rust/apps/bitcoin/src/transactions/parsed_tx.rs b/rust/apps/bitcoin/src/transactions/parsed_tx.rs
index 4e5d49a..befd174 100644
--- a/rust/apps/bitcoin/src/transactions/parsed_tx.rs
+++ b/rust/apps/bitcoin/src/transactions/parsed_tx.rs
@@ -18,6 +18,8 @@ pub struct ParsedInput {
pub address: Option<String>,
pub amount: String,
pub value: u64,
+ pub input_txid: String,
+ pub input_vout: u32,
pub path: Option<String>,
pub sign_status: (u32, u32),
pub is_multisig: bool,
@@ -61,6 +63,7 @@ pub struct OverviewTx {
pub is_multisig: bool,
pub sign_status: Option<String>,
pub need_sign: bool,
+ pub has_witness_only_inputs: bool,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -160,6 +163,7 @@ pub trait TxParser {
inputs: Vec<ParsedInput>,
outputs: Vec<ParsedOutput>,
network: &dyn NetworkT,
+ has_witness_only_inputs: bool,
) -> Result<ParsedTx> {
let total_input_value = inputs.iter().fold(0, |acc, cur| acc + cur.value);
let total_output_value = outputs.iter().fold(0, |acc, cur| acc + cur.value);
@@ -204,6 +208,7 @@ pub trait TxParser {
overview_to.sort();
overview_to.dedup();
let overview = OverviewTx {
+ has_witness_only_inputs,
sign_status: Self::get_sign_status_text(&inputs),
total_output_amount: Self::format_amount(overview_amount, network),
fee_amount: Self::format_amount(fee, network),
diff --git a/rust/apps/bitcoin/src/transactions/psbt/parsed_psbt.rs b/rust/apps/bitcoin/src/transactions/psbt/parsed_psbt.rs
index 13b48c4..f4ea652 100644
--- a/rust/apps/bitcoin/src/transactions/psbt/parsed_psbt.rs
+++ b/rust/apps/bitcoin/src/transactions/psbt/parsed_psbt.rs
@@ -28,10 +28,17 @@ impl TxParser for WrappedPsbt {
.enumerate()
.map(|(i, output)| self.parse_output(output, i, context, &network))
.collect::<Result<Vec<ParsedOutput>>>()?;
+ let has_witness_only_inputs = self
+ .psbt
+ .inputs
+ .iter()
+ .any(|input| input.witness_utxo.is_some() && input.non_witness_utxo.is_none());
match self.identify_fractal_bitcoin_tx() {
- Some(custom_net) => self.normalize(inputs, outputs, &custom_net),
- None => self.normalize(inputs, outputs, &network),
+ Some(custom_net) => {
+ self.normalize(inputs, outputs, &custom_net, has_witness_only_inputs)
+ }
+ None => self.normalize(inputs, outputs, &network, has_witness_only_inputs),
}
}
@@ -120,6 +127,11 @@ mod tests {
Psbt::from_unsigned_tx(tx).unwrap()
}
+ fn assert_witness_only_input(input: &bitcoin::psbt::Input) {
+ assert!(input.witness_utxo.is_some());
+ assert!(input.non_witness_utxo.is_none());
+ }
+
#[test]
fn test_parse_psbt() {
let psbt_hex = "70736274ff01005202000000016d41e6873468f85aff76d7709a93b47180ea0784edaac748228d2c474396ca550000000000fdffffff01a00f0000000000001600146623828c1f87be7841a9b1cc360d38ae0a8b6ed0000000000001011f6817000000000000160014d0c4a3ef09e997b6e99e397e518fe3e41a118ca1220602e7ab2537b5d49e970309aae06e9e49f36ce1c9febbd44ec8e0d1cca0b4f9c3191873c5da0a54000080010000800000008000000000000000000000";
@@ -139,6 +151,15 @@ mod tests {
multisig_wallet_config: None,
}))
.unwrap();
+ let first_psbt_input = wpsbt.psbt.inputs.first().unwrap();
+ assert_witness_only_input(first_psbt_input);
+ assert!(first_psbt_input
+ .witness_utxo
+ .as_ref()
+ .unwrap()
+ .script_pubkey
+ .is_p2wpkh());
+ assert!(result.overview.has_witness_only_inputs);
assert_eq!("0.00005992 tBTC", result.detail.total_input_amount);
assert_eq!("0.00004 tBTC", result.detail.total_output_amount);
assert_eq!("0.00001992 tBTC", result.detail.fee_amount);
@@ -184,6 +205,15 @@ mod tests {
multisig_wallet_config: None,
}))
.unwrap();
+ let first_psbt_input = wpsbt.psbt.inputs.first().unwrap();
+ assert_witness_only_input(first_psbt_input);
+ assert!(first_psbt_input
+ .witness_utxo
+ .as_ref()
+ .unwrap()
+ .script_pubkey
+ .is_p2wpkh());
+ assert!(result.overview.has_witness_only_inputs);
assert_eq!("0.00005992 tFB", result.detail.total_input_amount);
assert_eq!("0.00004 tFB", result.detail.total_output_amount);
assert_eq!("0.00001992 tFB", result.detail.fee_amount);
@@ -230,6 +260,15 @@ mod tests {
}))
.unwrap();
+ let first_psbt_input = wpsbt.psbt.inputs.first().unwrap();
+ assert_witness_only_input(first_psbt_input);
+ assert!(first_psbt_input
+ .witness_utxo
+ .as_ref()
+ .unwrap()
+ .script_pubkey
+ .is_p2wsh());
+ assert!(result.overview.has_witness_only_inputs);
assert_eq!("0.000142 BTC", result.detail.total_input_amount);
assert_eq!("0.00012797 BTC", result.detail.total_output_amount);
assert_eq!("Unsigned", result.detail.sign_status.unwrap());
@@ -256,6 +295,15 @@ mod tests {
}))
.unwrap();
+ let first_psbt_input = wpsbt.psbt.inputs.first().unwrap();
+ assert_witness_only_input(first_psbt_input);
+ assert!(first_psbt_input
+ .witness_utxo
+ .as_ref()
+ .unwrap()
+ .script_pubkey
+ .is_p2tr());
+ assert!(result.overview.has_witness_only_inputs);
assert_eq!("0.00006588 tBTC", result.detail.total_input_amount);
assert_eq!("0.000064 tBTC", result.detail.total_output_amount);
assert_eq!("0.00000188 tBTC", result.detail.fee_amount);
diff --git a/rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs b/rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs
index 771d475..52c0d51 100644
--- a/rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs
+++ b/rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs
@@ -98,12 +98,12 @@ impl WrappedPsbt {
self.calculate_address_for_input(input, network)?
};
let unsigned_tx = &self.psbt.unsigned_tx;
+ let tx_in = unsigned_tx
+ .input
+ .get(index)
+ .ok_or(BitcoinError::InvalidInput)?;
let mut value = 0u64;
if let Some(prev_tx) = &input.non_witness_utxo {
- let tx_in = unsigned_tx
- .input
- .get(index)
- .ok_or(BitcoinError::InvalidInput)?;
if !tx_in.previous_output.txid.eq(&prev_tx.compute_txid()) {
return Err(BitcoinError::InvalidInput);
}
@@ -126,6 +126,8 @@ impl WrappedPsbt {
address,
amount: Self::format_amount(value, network),
value,
+ input_txid: tx_in.previous_output.txid.to_string(),
+ input_vout: tx_in.previous_output.vout,
path: path.clone().map(|v| v.0),
sign_status,
is_multisig,
diff --git a/rust/rust_c/src/bitcoin/structs.rs b/rust/rust_c/src/bitcoin/structs.rs
index 37468a9..02f9ef0 100644
--- a/rust/rust_c/src/bitcoin/structs.rs
+++ b/rust/rust_c/src/bitcoin/structs.rs
@@ -62,6 +62,7 @@ pub struct DisplayTxOverview {
fee_larger_than_amount: bool,
sign_status: PtrString,
need_sign: bool,
+ has_witness_only_inputs: bool,
}
impl_c_ptr!(DisplayTxOverview);
@@ -92,6 +93,8 @@ pub struct DisplayTxDetailInput {
has_address: bool,
address: PtrString,
amount: PtrString,
+ input_txid: PtrString,
+ input_vout: u32,
is_mine: bool,
path: PtrString,
is_external: bool,
@@ -160,6 +163,7 @@ impl From<OverviewTx> for DisplayTxOverview {
null_mut()
},
need_sign: value.need_sign,
+ has_witness_only_inputs: value.has_witness_only_inputs,
}
}
}
@@ -205,6 +209,8 @@ impl From<ParsedInput> for DisplayTxDetailInput {
has_address: value.address.is_some(),
address: value.address.map(convert_c_char).unwrap_or(null_mut()),
amount: convert_c_char(value.amount),
+ input_txid: convert_c_char(value.input_txid),
+ input_vout: value.input_vout,
is_mine: value.path.is_some(),
path: value.path.map(convert_c_char).unwrap_or(null_mut()),
is_external: value.is_external,
@@ -289,6 +295,7 @@ impl Free for DisplayTxDetailInput {
unsafe fn free(&self) {
let _ = Box::from_raw(self.address);
let _ = Box::from_raw(self.amount);
+ let _ = Box::from_raw(self.input_txid);
let _ = Box::from_raw(self.path);
}
}
diff --git a/src/ui/gui_chain/gui_btc.c b/src/ui/gui_chain/gui_btc.c
index 73e31b7..24d4c33 100644
--- a/src/ui/gui_chain/gui_btc.c
+++ b/src/ui/gui_chain/gui_btc.c
@@ -10,6 +10,7 @@
#include "screen_manager.h"
#include "account_manager.h"
#include "gui_chain_components.h"
+#include "gui_qr_hintbox.h"
#include "gui_home_widgets.h"
#include "gui_transaction_detail_widgets.h"
#include "err_code.h"
@@ -67,7 +68,10 @@ static uint8_t *g_psbtBytes = NULL;
static uint32_t g_psbtBytesLen = 0;
static TransactionParseResult_DisplayTx *g_parseResult = NULL;
static TransactionParseResult_DisplayBtcMsg *g_parseMsgResult = NULL;
+static const char *g_checkInputValueHintText = "Please confirm each input value is correct.";
static bool IsMultiSigTx(DisplayTx *data);
+static bool NeedShowCheckInputValueHint(DisplayTx *data);
+static void OpenInputRefQrCode(lv_event_t *e);
static UREncodeResult *GetBtcSignDataDynamic(bool unLimit);
static void PreparePublicKeys(PtrT_CSliceFFI_ExtendedPublicKey public_keys, ExtendedPublicKey *keys);
@@ -763,6 +767,27 @@ static bool IsMultiSigTx(DisplayTx *data)
return data->overview->is_multisig;
}
+static bool NeedShowCheckInputValueHint(DisplayTx *data)
+{
+ return data->overview->has_witness_only_inputs;
+}
+
+static void OpenInputRefQrCode(lv_event_t *e)
+{
+ DisplayTxDetailInput *inputData = (DisplayTxDetailInput *)lv_event_get_user_data(e);
+ if (inputData == NULL || inputData->input_txid == NULL) {
+ return;
+ }
+
+ char url[128] = {0};
+ char outpoint[160] = {0};
+ snprintf_s(url, sizeof(url), "https://mempool.space/tx/%s", inputData->input_txid);
+ snprintf_s(outpoint, sizeof(outpoint), "Outpoint:\n%s:%u", inputData->input_txid,
+ inputData->input_vout);
+
+ GuiQRCodeHintBoxOpen(url, "Input Reference", outpoint);
+}
+
static bool IsAvalancheTx(DisplayTx *data)
{
return strcmp(data->overview->network, "Avalanche BTC") == 0;
@@ -838,7 +863,7 @@ static lv_obj_t *CreateAvalancheNoticeView(lv_obj_t *parent, lv_obj_t *lastView)
if (lastView == NULL) {
lv_obj_align(noticeContainer, LV_ALIGN_DEFAULT, 0, 0);
} else {
- lv_obj_align_to(noticeContainer, lastView, LV_ALIGN_OUT_BOTTOM_MID, 0, 16);
+ lv_obj_align_to(noticeContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
}
SetContainerDefaultStyle(noticeContainer);
@@ -856,13 +881,48 @@ static lv_obj_t *CreateAvalancheNoticeView(lv_obj_t *parent, lv_obj_t *lastView)
return noticeContainer;
}
+static lv_obj_t *CreateCheckInputValueHintView(lv_obj_t *parent, lv_obj_t *lastView)
+{
+ lv_obj_t *hintView = CreateNoticeCard(parent, g_checkInputValueHintText);
+ if (lastView == NULL) {
+ lv_obj_align(hintView, LV_ALIGN_DEFAULT, 0, 0);
+ } else {
+ lv_obj_align_to(hintView, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
+ }
+
+ return hintView;
+}
+
+static lv_obj_t *CreateInputRefView(lv_obj_t *parent, lv_obj_t *lastView, DisplayTxDetailInput *inputData)
+{
+ lv_obj_t *inputRefContainer = GuiCreateContainerWithParent(parent, 160, 30);
+ lv_obj_set_style_bg_opa(inputRefContainer, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
+ lv_obj_set_style_border_width(inputRefContainer, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
+ lv_obj_set_style_pad_all(inputRefContainer, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
+ lv_obj_clear_flag(inputRefContainer, LV_OBJ_FLAG_SCROLLABLE);
+ lv_obj_add_flag(inputRefContainer, LV_OBJ_FLAG_CLICKABLE);
+ lv_obj_align_to(inputRefContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 12);
+ lv_obj_add_event_cb(inputRefContainer, OpenInputRefQrCode, LV_EVENT_CLICKED, inputData);
+
+ lv_obj_t *inputRefLabel = GuiCreateIllustrateLabel(inputRefContainer, "Input Ref");
+ lv_obj_set_style_text_color(inputRefLabel, lv_color_hex(0x1BE0C6), LV_PART_MAIN);
+ lv_obj_align(inputRefLabel, LV_ALIGN_LEFT_MID, 0, 0);
+ lv_obj_clear_flag(inputRefLabel, LV_OBJ_FLAG_CLICKABLE);
+
+ lv_obj_t *inputRefIcon = GuiCreateImg(inputRefContainer, &imgQrcodeTurquoise);
+ lv_obj_align_to(inputRefIcon, inputRefLabel, LV_ALIGN_OUT_RIGHT_MID, 12, 0);
+ lv_obj_clear_flag(inputRefIcon, LV_OBJ_FLAG_CLICKABLE);
+
+ return inputRefContainer;
+}
+
static lv_obj_t *CreateOverviewAmountView(lv_obj_t *parent, DisplayTxOverview *overviewData, lv_obj_t *lastView)
{
lv_obj_t *amountContainer = GuiCreateContainerWithParent(parent, 408, 144);
if (lastView == NULL) {
lv_obj_align(amountContainer, LV_ALIGN_DEFAULT, 0, 0);
} else {
- lv_obj_align_to(amountContainer, lastView, LV_ALIGN_OUT_BOTTOM_MID, 0, 16);
+ lv_obj_align_to(amountContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
}
SetContainerDefaultStyle(amountContainer);
@@ -919,7 +979,7 @@ static lv_obj_t *CreateNetworkView(lv_obj_t *parent, char *network, lv_obj_t *la
if (lastView == NULL) {
lv_obj_align(networkContainer, LV_ALIGN_DEFAULT, 0, 0);
} else {
- lv_obj_align_to(networkContainer, lastView, LV_ALIGN_OUT_BOTTOM_MID, 0, 16);
+ lv_obj_align_to(networkContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
}
SetContainerDefaultStyle(networkContainer);
@@ -1098,7 +1158,7 @@ static lv_obj_t *CreateOverviewToView(lv_obj_t *parent, DisplayTxOverview *overv
static lv_obj_t *CreateDetailAmountView(lv_obj_t *parent, DisplayTxDetail *detailData, lv_obj_t *lastView)
{
lv_obj_t *amountContainer = GuiCreateContainerWithParent(parent, 408, 138);
- lv_obj_align_to(amountContainer, lastView, LV_ALIGN_OUT_BOTTOM_MID, 0, 16);
+ lv_obj_align_to(amountContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
SetContainerDefaultStyle(amountContainer);
lv_obj_t *label = lv_label_create(amountContainer);
@@ -1218,7 +1278,8 @@ static lv_obj_t *CreateDetailFromView(lv_obj_t *parent, DisplayTxDetail *detailD
lv_obj_align_to(pathLabel, addressLabel, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 4);
lv_obj_update_layout(pathLabel);
- int pathLabelBottom = lv_obj_get_y2(pathLabel);
+ lv_obj_t *inputRefLabel = CreateInputRefView(formInnerContainer, pathLabel, &from->data[i]);
+ int pathLabelBottom = lv_obj_get_y2(inputRefLabel);
lv_obj_set_height(formInnerContainer, pathLabelBottom);
@@ -1322,6 +1383,10 @@ void GuiBtcTxOverview(lv_obj_t *parent, void *totalData)
lv_obj_t *lastView = NULL;
+ if (NeedShowCheckInputValueHint(txData)) {
+ lastView = CreateCheckInputValueHintView(parent, lastView);
+ }
+
if (IsMultiSigTx(txData)) {
lastView = CreateSignStatusView(parent, overviewData->sign_status);
}
@@ -1367,4 +1432,4 @@ void GuiBtcMsg(lv_obj_t *parent, void *totalData)
lastView = CreateTransactionItemViewWithHint(parent, _("Address"), msgData->address, lastView, _("sign_message_check_address"));
}
lastView = CreateTransactionItemView(parent, _("Message"), msgData->detail, lastView);
-}
\ No newline at end of file
+}
diff --git a/src/ui/gui_components/gui_qr_hintbox.c b/src/ui/gui_components/gui_qr_hintbox.c
index 8251de1..c57b8c2 100644
--- a/src/ui/gui_components/gui_qr_hintbox.c
+++ b/src/ui/gui_components/gui_qr_hintbox.c
@@ -42,7 +42,6 @@ bool GuiQRHintBoxIsActive()
return g_qrHintBox != NULL;
}
-
void GuiQRCodeHintBoxOpenBig(const char *qrdata, const char *title, const char *content, const char *url)
{
lv_obj_t *button, *qrCodeCont, *qrCode;
@@ -196,4 +195,4 @@ void GuiQRCodeHintBoxOpen(const char *qrdata, const char *title, const char *sub
lv_label_set_text(g_qrHintBoxTitle, title);
lv_label_set_text(g_qrHintBoxSubTitle, subtitle);
}
-}
\ No newline at end of file
+}
diff --git a/src/ui/gui_components/gui_qr_hintbox.h b/src/ui/gui_components/gui_qr_hintbox.h
index 52dee1c..cff75ff 100644
--- a/src/ui/gui_components/gui_qr_hintbox.h
+++ b/src/ui/gui_components/gui_qr_hintbox.h
@@ -7,4 +7,4 @@ void GuiQRCodeHintBoxOpenBig(const char *qrdata, const char *title, const char *
void GuiNormalHitBoxOpen(const char *title, const char *content);
bool GuiQRHintBoxIsActive();
-#endif
\ No newline at end of file
+#endif
diff --git a/ui_simulator/simulator_model.c b/ui_simulator/simulator_model.c
index 5461e76..553f5ee 100644
--- a/ui_simulator/simulator_model.c
+++ b/ui_simulator/simulator_model.c
@@ -21,7 +21,7 @@ bool g_reboot = false;
bool g_otpProtect = false;
// Comment out this macro if you need to retrieve data from the file
-#define GET_QR_DATA_FROM_SCREEN
+// #define GET_QR_DATA_FROM_SCREEN
void OTP_PowerOn(void)
{
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.