feat(core): show maximal coordinator fee rate
What changed, and why it matters
This commit adds a new line on the Trezor screen when authorizing a CoinJoin: it now shows the 'Max coordinator fee rate' alongside the existing 'Max rounds' and 'Max mining fee'. This is a user-interface improvement that gives the device owner more information before they approve a CoinJoin session. It does not change any security logic, fee enforcement, or authorization checks; it only displays an extra value that was already part of the authorization message.
No security action required. Treat as a normal feature/UI improvement. Reviewers may optionally verify that the new string is correctly formatted and that all four layouts display it without truncation.
Security signals we found
UI-only change: no authorization or fee-enforcement logic altered
New user-visible field surfaces coordinator fee rate that was already present in the protocol message
No new parsing, no buffer-size changes, no cryptographic operations
Translation signature update is routine metadata change
Evidence from the diff
The change threads a new string parameter, max_coordinator_fee_pct, through the confirm_coinjoin UI flow. authorize_coinjoin.py formats msg.max_coordinator_fee_rate using format_amount(…, FEE_RATE_DECIMALS) + ‘%’ and passes it to the layout confirm_coinjoin function. The Rust UI layer for all four device layouts (Bolt, Caesar, Delizia, Eckhart) accepts the new parameter and renders a ‘Max coordinator fee’ label with the supplied percentage. Translation keys, generated translation tables, qstr mappings, mocks, and translation signatures are updated accordingly. No validation, enforcement, or protocol logic is modified.
Changed components
core/src/apps/bitcoin/authorize_coinjoin.pycore/src/trezor/ui/layouts/{bolt,caesar,delizia,eckhart}/__init__.pycore/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/ui/{layout_bolt,layout_caesar,layout_delizia,layout_eckhart}/ui_firmware.rscore/embed/rust/src/ui/ui_firmware.rscore/embed/rust/librust_qstr.hcore/embed/rust/src/translations/generated/translated_string.rscore/translations/en.jsoncore/translations/order.jsoncore/translations/signatures.jsoncore/mocks/generated/trezorui_api.pyicore/mocks/trezortranslate_keys.pyiInspect captured patch +448 / −371
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index ea849c67..136a129f 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -297,6 +297,7 @@ static void _librust_qstrs(void) {
MP_QSTR_code;
MP_QSTR_coinjoin__access_account;
MP_QSTR_coinjoin__do_not_disconnect;
+ MP_QSTR_coinjoin__max_coordinator_fee_pct;
MP_QSTR_coinjoin__max_mining_fee;
MP_QSTR_coinjoin__max_rounds;
MP_QSTR_coinjoin__title;
@@ -477,6 +478,7 @@ static void _librust_qstrs(void) {
MP_QSTR_lockscreen__title_locked;
MP_QSTR_lockscreen__title_not_connected;
MP_QSTR_lockscreen__unlock;
+ MP_QSTR_max_coordinator_fee_pct;
MP_QSTR_max_count;
MP_QSTR_max_feerate;
MP_QSTR_max_len;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index a1392cae..eeda8b72 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1624,46 +1624,47 @@ pub enum TranslatedString {
tron__owner_address = 1238, // "Owner address"
#[cfg(feature = "universal_fw")]
ethereum__eip_712_empty_domain = 1239, // "dApp did not identify itself!"
+ coinjoin__max_coordinator_fee_pct = 1240, // "Max coordinator fee"
#[cfg(feature = "universal_fw")]
- ethereum__all_evms = 1240, // "All EVMs"
+ ethereum__all_evms = 1241, // "All EVMs"
#[cfg(feature = "universal_fw")]
- ethereum__auth_done = 1241, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 authorization signed.", "Eckhart": "EIP-7702 authorization signed."}
+ ethereum__auth_done = 1242, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 authorization signed.", "Eckhart": "EIP-7702 authorization signed."}
#[cfg(feature = "universal_fw")]
- ethereum__auth_title = 1242, // "EIP-7702 Authorization"
+ ethereum__auth_title = 1243, // "EIP-7702 Authorization"
#[cfg(feature = "universal_fw")]
- ethereum__auth_warn = 1243, // "Authorize your account to a third-party contract using EIP-7702?"
+ ethereum__auth_warn = 1244, // "Authorize your account to a third-party contract using EIP-7702?"
#[cfg(feature = "universal_fw")]
- ethereum__calldata_digest = 1244, // "ERC-8213 digest"
+ ethereum__calldata_digest = 1245, // "ERC-8213 digest"
#[cfg(feature = "universal_fw")]
- ethereum__delegate_done = 1245, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 delegation signed.", "Eckhart": "EIP-7702 delegation signed."}
+ ethereum__delegate_done = 1246, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 delegation signed.", "Eckhart": "EIP-7702 delegation signed."}
#[cfg(feature = "universal_fw")]
- ethereum__delegate_title = 1246, // "EIP-7702 Delegation"
+ ethereum__delegate_title = 1247, // "EIP-7702 Delegation"
#[cfg(feature = "universal_fw")]
- ethereum__delegate_warn = 1247, // "Delegate your account to a third-party contract?"
+ ethereum__delegate_warn = 1248, // "Delegate your account to a third-party contract?"
#[cfg(feature = "universal_fw")]
- ethereum__delegating = 1248, // "Delegating"
+ ethereum__delegating = 1249, // "Delegating"
#[cfg(feature = "universal_fw")]
- ethereum__network = 1249, // "Network"
+ ethereum__network = 1250, // "Network"
#[cfg(feature = "universal_fw")]
- ethereum__revoke_done = 1250, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 revocation signed.", "Eckhart": "EIP-7702 revocation signed."}
+ ethereum__revoke_done = 1251, // {"Bolt": "", "Caesar": "", "Delizia": "EIP-7702 revocation signed.", "Eckhart": "EIP-7702 revocation signed."}
#[cfg(feature = "universal_fw")]
- ethereum__revoke_title = 1251, // "EIP-7702 Revocation"
+ ethereum__revoke_title = 1252, // "EIP-7702 Revocation"
#[cfg(feature = "universal_fw")]
- ethereum__revoke_warn = 1252, // "Revoke {0} from smart account delegation?"
+ ethereum__revoke_warn = 1253, // "Revoke {0} from smart account delegation?"
#[cfg(feature = "universal_fw")]
- ethereum__smart_info = 1253, // "Smart account contract info"
+ ethereum__smart_info = 1254, // "Smart account contract info"
#[cfg(feature = "universal_fw")]
- ethereum__to = 1254, // "To"
- pin__reenter_new_description = 1255, // {"Bolt": "", "Caesar": "", "Delizia": "Re-enter new PIN.", "Eckhart": ""}
+ ethereum__to = 1255, // "To"
+ pin__reenter_new_description = 1256, // {"Bolt": "", "Caesar": "", "Delizia": "Re-enter new PIN.", "Eckhart": ""}
#[cfg(feature = "universal_fw")]
- stellar__ext_auth = 1256, // "External Authorizations"
+ stellar__ext_auth = 1257, // "External Authorizations"
#[cfg(feature = "universal_fw")]
- stellar__ext_auth_message = 1257, // "Transaction contains additional invocations authorized by external means."
+ stellar__ext_auth_message = 1258, // "Transaction contains additional invocations authorized by external means."
#[cfg(feature = "universal_fw")]
- stellar__invoke_contract = 1258, // "Invoke Contract"
- words__arguments = 1259, // "Arguments"
- words__authorization = 1260, // "Authorization"
- words__function = 1261, // "Function"
+ stellar__invoke_contract = 1259, // "Invoke Contract"
+ words__arguments = 1260, // "Arguments"
+ words__authorization = 1261, // "Authorization"
+ words__function = 1262, // "Function"
}
impl TranslatedString {
@@ -2911,6 +2912,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -4178,28 +4180,29 @@ impl TranslatedString {
18608,
18621,
18650,
- 18658,
- 18658,
- 18680,
- 18744,
- 18759,
- 18759,
+ 18669,
+ 18677,
+ 18677,
+ 18699,
+ 18763,
18778,
- 18826,
- 18836,
- 18843,
- 18843,
+ 18778,
+ 18797,
+ 18845,
+ 18855,
+ 18862,
18862,
- 18903,
- 18930,
- 18932,
- 18932,
- 18955,
- 19028,
- 19043,
- 19052,
- 19065,
- 19073,
+ 18881,
+ 18922,
+ 18949,
+ 18951,
+ 18951,
+ 18974,
+ 19047,
+ 19062,
+ 19071,
+ 19084,
+ 19092,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -5444,6 +5447,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -6711,28 +6715,29 @@ impl TranslatedString {
18608,
18621,
18650,
- 18658,
- 18658,
- 18680,
- 18744,
- 18759,
- 18759,
+ 18669,
+ 18677,
+ 18677,
+ 18699,
+ 18763,
+ 18778,
18778,
- 18826,
- 18836,
- 18843,
- 18843,
+ 18797,
+ 18845,
+ 18855,
18862,
- 18903,
- 18930,
- 18932,
- 18932,
- 18955,
- 19028,
- 19043,
- 19052,
- 19065,
- 19073,
+ 18862,
+ 18881,
+ 18922,
+ 18949,
+ 18951,
+ 18951,
+ 18974,
+ 19047,
+ 19062,
+ 19071,
+ 19084,
+ 19092,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -7977,6 +7982,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -9244,28 +9250,29 @@ impl TranslatedString {
18608,
18621,
18650,
- 18658,
- 18658,
- 18680,
- 18744,
- 18759,
- 18759,
+ 18669,
+ 18677,
+ 18677,
+ 18699,
+ 18763,
+ 18778,
18778,
- 18826,
- 18836,
- 18843,
- 18843,
+ 18797,
+ 18845,
+ 18855,
+ 18862,
18862,
- 18903,
- 18930,
- 18932,
- 18932,
- 18955,
- 19028,
- 19043,
- 19052,
- 19065,
- 19073,
+ 18881,
+ 18922,
+ 18949,
+ 18951,
+ 18951,
+ 18974,
+ 19047,
+ 19062,
+ 19071,
+ 19084,
+ 19092,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -10510,6 +10517,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -11777,28 +11785,29 @@ impl TranslatedString {
18608,
18621,
18650,
- 18658,
- 18658,
- 18680,
- 18744,
- 18759,
- 18759,
+ 18669,
+ 18677,
+ 18677,
+ 18699,
+ 18763,
18778,
- 18826,
- 18836,
- 18843,
- 18843,
+ 18778,
+ 18797,
+ 18845,
+ 18855,
+ 18862,
18862,
- 18903,
- 18930,
- 18932,
- 18932,
- 18955,
- 19028,
- 19043,
- 19052,
- 19065,
- 19073,
+ 18881,
+ 18922,
+ 18949,
+ 18951,
+ 18951,
+ 18974,
+ 19047,
+ 19062,
+ 19071,
+ 19084,
+ 19092,
];
} else if #[cfg(feature = "layout_caesar")] {
@@ -13044,6 +13053,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -14311,28 +14321,29 @@ impl TranslatedString {
16399,
16412,
16441,
- 16449,
- 16449,
- 16471,
- 16535,
- 16550,
- 16550,
+ 16460,
+ 16468,
+ 16468,
+ 16490,
+ 16554,
16569,
- 16617,
- 16627,
- 16634,
- 16634,
+ 16569,
+ 16588,
+ 16636,
+ 16646,
16653,
- 16694,
- 16721,
- 16723,
- 16723,
- 16746,
- 16819,
- 16834,
- 16843,
- 16856,
- 16864,
+ 16653,
+ 16672,
+ 16713,
+ 16740,
+ 16742,
+ 16742,
+ 16765,
+ 16838,
+ 16853,
+ 16862,
+ 16875,
+ 16883,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -15577,6 +15588,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -16844,28 +16856,29 @@ impl TranslatedString {
16399,
16412,
16441,
- 16449,
- 16449,
- 16471,
- 16535,
- 16550,
- 16550,
+ 16460,
+ 16468,
+ 16468,
+ 16490,
+ 16554,
16569,
- 16617,
- 16627,
- 16634,
- 16634,
+ 16569,
+ 16588,
+ 16636,
+ 16646,
16653,
- 16694,
- 16721,
- 16723,
- 16723,
- 16746,
- 16819,
- 16834,
- 16843,
- 16856,
- 16864,
+ 16653,
+ 16672,
+ 16713,
+ 16740,
+ 16742,
+ 16742,
+ 16765,
+ 16838,
+ 16853,
+ 16862,
+ 16875,
+ 16883,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -18110,6 +18123,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -19377,28 +19391,29 @@ impl TranslatedString {
16399,
16412,
16441,
- 16449,
- 16449,
- 16471,
- 16535,
- 16550,
- 16550,
+ 16460,
+ 16468,
+ 16468,
+ 16490,
+ 16554,
16569,
- 16617,
- 16627,
- 16634,
- 16634,
+ 16569,
+ 16588,
+ 16636,
+ 16646,
16653,
- 16694,
- 16721,
- 16723,
- 16723,
- 16746,
- 16819,
- 16834,
- 16843,
- 16856,
- 16864,
+ 16653,
+ 16672,
+ 16713,
+ 16740,
+ 16742,
+ 16742,
+ 16765,
+ 16838,
+ 16853,
+ 16862,
+ 16875,
+ 16883,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -20643,6 +20658,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"",
"EIP-7702 Authorization",
@@ -21910,28 +21926,29 @@ impl TranslatedString {
16399,
16412,
16441,
- 16449,
- 16449,
- 16471,
- 16535,
- 16550,
- 16550,
+ 16460,
+ 16468,
+ 16468,
+ 16490,
+ 16554,
16569,
- 16617,
- 16627,
- 16634,
- 16634,
+ 16569,
+ 16588,
+ 16636,
+ 16646,
16653,
- 16694,
- 16721,
- 16723,
- 16723,
- 16746,
- 16819,
- 16834,
- 16843,
- 16856,
- 16864,
+ 16653,
+ 16672,
+ 16713,
+ 16740,
+ 16742,
+ 16742,
+ 16765,
+ 16838,
+ 16853,
+ 16862,
+ 16875,
+ 16883,
];
} else if #[cfg(feature = "layout_delizia")] {
@@ -23177,6 +23194,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -24444,28 +24462,29 @@ impl TranslatedString {
18396,
18409,
18438,
- 18446,
- 18476,
- 18498,
- 18562,
- 18577,
- 18604,
+ 18457,
+ 18465,
+ 18495,
+ 18517,
+ 18581,
+ 18596,
18623,
- 18671,
- 18681,
- 18688,
- 18715,
+ 18642,
+ 18690,
+ 18700,
+ 18707,
18734,
- 18775,
- 18802,
- 18804,
+ 18753,
+ 18794,
18821,
- 18844,
- 18917,
- 18932,
- 18941,
- 18954,
- 18962,
+ 18823,
+ 18840,
+ 18863,
+ 18936,
+ 18951,
+ 18960,
+ 18973,
+ 18981,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -25710,6 +25729,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -26977,28 +26997,29 @@ impl TranslatedString {
18396,
18409,
18438,
- 18446,
- 18476,
- 18498,
- 18562,
- 18577,
- 18604,
+ 18457,
+ 18465,
+ 18495,
+ 18517,
+ 18581,
+ 18596,
18623,
- 18671,
- 18681,
- 18688,
- 18715,
+ 18642,
+ 18690,
+ 18700,
+ 18707,
18734,
- 18775,
- 18802,
- 18804,
+ 18753,
+ 18794,
18821,
- 18844,
- 18917,
- 18932,
- 18941,
- 18954,
- 18962,
+ 18823,
+ 18840,
+ 18863,
+ 18936,
+ 18951,
+ 18960,
+ 18973,
+ 18981,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -28243,6 +28264,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -29510,28 +29532,29 @@ impl TranslatedString {
18396,
18409,
18438,
- 18446,
- 18476,
- 18498,
- 18562,
- 18577,
- 18604,
+ 18457,
+ 18465,
+ 18495,
+ 18517,
+ 18581,
+ 18596,
18623,
- 18671,
- 18681,
- 18688,
- 18715,
+ 18642,
+ 18690,
+ 18700,
+ 18707,
18734,
- 18775,
- 18802,
- 18804,
+ 18753,
+ 18794,
18821,
- 18844,
- 18917,
- 18932,
- 18941,
- 18954,
- 18962,
+ 18823,
+ 18840,
+ 18863,
+ 18936,
+ 18951,
+ 18960,
+ 18973,
+ 18981,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -30776,6 +30799,7 @@ impl TranslatedString {
"",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -32043,28 +32067,29 @@ impl TranslatedString {
18396,
18409,
18438,
- 18446,
- 18476,
- 18498,
- 18562,
- 18577,
- 18604,
+ 18457,
+ 18465,
+ 18495,
+ 18517,
+ 18581,
+ 18596,
18623,
- 18671,
- 18681,
- 18688,
- 18715,
+ 18642,
+ 18690,
+ 18700,
+ 18707,
18734,
- 18775,
- 18802,
- 18804,
+ 18753,
+ 18794,
18821,
- 18844,
- 18917,
- 18932,
- 18941,
- 18954,
- 18962,
+ 18823,
+ 18840,
+ 18863,
+ 18936,
+ 18951,
+ 18960,
+ 18973,
+ 18981,
];
} else if #[cfg(feature = "layout_eckhart")] {
@@ -33310,6 +33335,7 @@ impl TranslatedString {
"This is NOT your address",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -34577,28 +34603,29 @@ impl TranslatedString {
19774,
19787,
19816,
- 19824,
- 19854,
- 19876,
- 19940,
- 19955,
- 19982,
+ 19835,
+ 19843,
+ 19873,
+ 19895,
+ 19959,
+ 19974,
20001,
- 20049,
- 20059,
- 20066,
- 20093,
+ 20020,
+ 20068,
+ 20078,
+ 20085,
20112,
- 20153,
- 20180,
- 20182,
- 20182,
- 20205,
- 20278,
- 20293,
- 20302,
- 20315,
- 20323,
+ 20131,
+ 20172,
+ 20199,
+ 20201,
+ 20201,
+ 20224,
+ 20297,
+ 20312,
+ 20321,
+ 20334,
+ 20342,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -35843,6 +35870,7 @@ impl TranslatedString {
"This is NOT your address",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -37110,28 +37138,29 @@ impl TranslatedString {
19774,
19787,
19816,
- 19824,
- 19854,
- 19876,
- 19940,
- 19955,
- 19982,
+ 19835,
+ 19843,
+ 19873,
+ 19895,
+ 19959,
+ 19974,
20001,
- 20049,
- 20059,
- 20066,
- 20093,
+ 20020,
+ 20068,
+ 20078,
+ 20085,
20112,
- 20153,
- 20180,
- 20182,
- 20182,
- 20205,
- 20278,
- 20293,
- 20302,
- 20315,
- 20323,
+ 20131,
+ 20172,
+ 20199,
+ 20201,
+ 20201,
+ 20224,
+ 20297,
+ 20312,
+ 20321,
+ 20334,
+ 20342,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -38376,6 +38405,7 @@ impl TranslatedString {
"This is NOT your address",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -39643,28 +39673,29 @@ impl TranslatedString {
19774,
19787,
19816,
- 19824,
- 19854,
- 19876,
- 19940,
- 19955,
- 19982,
+ 19835,
+ 19843,
+ 19873,
+ 19895,
+ 19959,
+ 19974,
20001,
- 20049,
- 20059,
- 20066,
- 20093,
+ 20020,
+ 20068,
+ 20078,
+ 20085,
20112,
- 20153,
- 20180,
- 20182,
- 20182,
- 20205,
- 20278,
- 20293,
- 20302,
- 20315,
- 20323,
+ 20131,
+ 20172,
+ 20199,
+ 20201,
+ 20201,
+ 20224,
+ 20297,
+ 20312,
+ 20321,
+ 20334,
+ 20342,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -40909,6 +40940,7 @@ impl TranslatedString {
"This is NOT your address",
"Owner address",
"dApp did not identify itself!",
+ "Max coordinator fee",
"All EVMs",
"EIP-7702 authorization signed.",
"EIP-7702 Authorization",
@@ -42176,28 +42208,29 @@ impl TranslatedString {
19774,
19787,
19816,
- 19824,
- 19854,
- 19876,
- 19940,
- 19955,
- 19982,
+ 19835,
+ 19843,
+ 19873,
+ 19895,
+ 19959,
+ 19974,
20001,
- 20049,
- 20059,
- 20066,
- 20093,
+ 20020,
+ 20068,
+ 20078,
+ 20085,
20112,
- 20153,
- 20180,
- 20182,
- 20182,
- 20205,
- 20278,
- 20293,
- 20302,
- 20315,
- 20323,
+ 20131,
+ 20172,
+ 20199,
+ 20201,
+ 20201,
+ 20224,
+ 20297,
+ 20312,
+ 20321,
+ 20334,
+ 20342,
];
}
@@ -42593,6 +42626,7 @@ impl TranslatedString {
(Qstr::MP_QSTR_cardano__x_of_y_signatures_template, Self::cardano__x_of_y_signatures_template),
(Qstr::MP_QSTR_coinjoin__access_account, Self::coinjoin__access_account),
(Qstr::MP_QSTR_coinjoin__do_not_disconnect, Self::coinjoin__do_not_disconnect),
+ (Qstr::MP_QSTR_coinjoin__max_coordinator_fee_pct, Self::coinjoin__max_coordinator_fee_pct),
(Qstr::MP_QSTR_coinjoin__max_mining_fee, Self::coinjoin__max_mining_fee),
(Qstr::MP_QSTR_coinjoin__max_rounds, Self::coinjoin__max_rounds),
(Qstr::MP_QSTR_coinjoin__title, Self::coinjoin__title),
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 8d5d3afd..6e0aa63e 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -233,8 +233,11 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
let block = move |_args: &[Obj], kwargs: &Map| {
let max_rounds: TString = kwargs.get(Qstr::MP_QSTR_max_rounds)?.try_into()?;
let max_feerate: TString = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
+ let max_coordinator_fee_pct: TString = kwargs
+ .get(Qstr::MP_QSTR_max_coordinator_fee_pct)?
+ .try_into()?;
- let layout = ModelUI::confirm_coinjoin(max_rounds, max_feerate)?;
+ let layout = ModelUI::confirm_coinjoin(max_rounds, max_feerate, max_coordinator_fee_pct)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@@ -1582,6 +1585,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// *,
/// max_rounds: str,
/// max_feerate: str,
+ /// max_coordinator_fee_pct: str,
/// ) -> LayoutContext[UiResult]:
/// """Confirm coinjoin authorization."""
Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(),
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index 888815fa..9e0a233d 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -175,12 +175,15 @@ impl FirmwareUI for UIBolt {
fn confirm_coinjoin(
max_rounds: TString<'static>,
max_feerate: TString<'static>,
+ max_coordinator_fee_pct: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_rounds),
Paragraph::new(&theme::TEXT_MONO, max_rounds),
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_mining_fee),
Paragraph::new(&theme::TEXT_MONO, max_feerate),
+ Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_coordinator_fee_pct),
+ Paragraph::new(&theme::TEXT_MONO, max_coordinator_fee_pct),
]);
let layout = RootComponent::new(Frame::left_aligned(
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index a0f4270a..e89d1a06 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -207,15 +207,19 @@ impl FirmwareUI for UICaesar {
fn confirm_coinjoin(
max_rounds: TString<'static>,
max_feerate: TString<'static>,
+ max_coordinator_fee_pct: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
- // Decreasing bottom padding between paragraphs to fit one screen
let paragraphs = Paragraphs::new([
+ // Decreasing bottom padding between paragraphs to fit one screen:
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_rounds).with_bottom_padding(2),
Paragraph::new(&theme::TEXT_MONO, max_rounds),
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_mining_fee)
.with_bottom_padding(2)
.no_break(),
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
+ // Shown in a separate screen:
+ Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_coordinator_fee_pct),
+ Paragraph::new(&theme::TEXT_MONO, max_coordinator_fee_pct),
]);
content_in_button_page(
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index 8211f057..d275ff15 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -220,12 +220,15 @@ impl FirmwareUI for UIDelizia {
fn confirm_coinjoin(
max_rounds: TString<'static>,
max_feerate: TString<'static>,
+ max_coordinator_fee_pct: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = ParagraphVecShort::from_iter([
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_rounds),
Paragraph::new(&theme::TEXT_MONO, max_rounds),
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_mining_fee),
Paragraph::new(&theme::TEXT_MONO, max_feerate),
+ Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_coordinator_fee_pct),
+ Paragraph::new(&theme::TEXT_MONO, max_coordinator_fee_pct),
])
.into_paragraphs();
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index ee213cec..e58497c7 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -143,6 +143,7 @@ impl FirmwareUI for UIEckhart {
fn confirm_coinjoin(
max_rounds: TString<'static>,
max_feerate: TString<'static>,
+ max_coordinator_fee_pct: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_REGULAR, TR::coinjoin__max_rounds)
@@ -153,7 +154,12 @@ impl FirmwareUI for UIEckhart {
Paragraph::new(&theme::TEXT_REGULAR, TR::coinjoin__max_mining_fee)
.with_bottom_padding(theme::PROP_INNER_SPACING)
.no_break(),
- Paragraph::new(&theme::TEXT_MONO_LIGHT, max_feerate),
+ Paragraph::new(&theme::TEXT_MONO_LIGHT, max_feerate)
+ .with_bottom_padding(theme::PROPS_SPACING),
+ Paragraph::new(&theme::TEXT_REGULAR, TR::coinjoin__max_coordinator_fee_pct)
+ .with_bottom_padding(theme::PROP_INNER_SPACING)
+ .no_break(),
+ Paragraph::new(&theme::TEXT_MONO_LIGHT, max_coordinator_fee_pct),
])
.with_placement(LinearPlacement::vertical());
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index 3a9fa644..8b7081ed 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -92,6 +92,7 @@ pub trait FirmwareUI {
fn confirm_coinjoin(
max_rounds: TString<'static>,
max_feerate: TString<'static>,
+ max_coordinator_fee_pct: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
fn confirm_emphasized(
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index e1d4d796..ef41b128 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -228,6 +228,7 @@ def confirm_coinjoin(
*,
max_rounds: str,
max_feerate: str,
+ max_coordinator_fee_pct: str,
) -> LayoutContext[UiResult]:
"""Confirm coinjoin authorization."""
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index e1e1ed35..be2fba9c 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -269,6 +269,7 @@ class TR:
cardano__x_of_y_signatures_template: str = "Requires {0} out of {1} signatures."
coinjoin__access_account: str = "Access your coinjoin account?"
coinjoin__do_not_disconnect: str = "Do not disconnect your Trezor!"
+ coinjoin__max_coordinator_fee_pct: str = "Max coordinator fee"
coinjoin__max_mining_fee: str = "Max mining fee"
coinjoin__max_rounds: str = "Max rounds"
coinjoin__title: str = "Authorize coinjoin"
diff --git a/core/src/apps/bitcoin/authorize_coinjoin.py b/core/src/apps/bitcoin/authorize_coinjoin.py
index f2c934c5..2e353e88 100644
--- a/core/src/apps/bitcoin/authorize_coinjoin.py
+++ b/core/src/apps/bitcoin/authorize_coinjoin.py
@@ -22,6 +22,7 @@ async def authorize_coinjoin(
from trezor import TR
from trezor.enums import ButtonRequestType
from trezor.messages import Success
+ from trezor.strings import format_amount
from trezor.ui.layouts import confirm_coinjoin, confirm_metadata
from trezor.wire import DataError
@@ -65,7 +66,10 @@ async def authorize_coinjoin(
msg.max_fee_per_kvbyte / 1000, coin, include_shortcut=True
)
- await confirm_coinjoin(msg.max_rounds, max_fee_per_vbyte)
+ max_coordinator_fee_pct = (
+ format_amount(msg.max_coordinator_fee_rate, FEE_RATE_DECIMALS) + "%"
+ )
+ await confirm_coinjoin(msg.max_rounds, max_fee_per_vbyte, max_coordinator_fee_pct)
validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH
await validate_path(
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index e08a1cab..4d206c8d 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -2044,10 +2044,13 @@ async def confirm_modify_fee(
)
-async def confirm_coinjoin(max_rounds: int, max_fee_per_vbyte: str) -> None:
+async def confirm_coinjoin(
+ max_rounds: int, max_fee_per_vbyte: str, max_coordinator_fee_pct: str
+) -> None:
with trezorui_api.confirm_coinjoin(
max_rounds=str(max_rounds),
max_feerate=max_fee_per_vbyte,
+ max_coordinator_fee_pct=max_coordinator_fee_pct,
) as layout:
return await raise_if_not_confirmed(layout, "coinjoin_final", BR_CODE_OTHER)
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 9d52fe6f..80ddd528 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -2124,10 +2124,13 @@ async def confirm_modify_fee(
)
-async def confirm_coinjoin(max_rounds: int, max_fee_per_vbyte: str) -> None:
+async def confirm_coinjoin(
+ max_rounds: int, max_fee_per_vbyte: str, max_coordinator_fee_pct: str
+) -> None:
with trezorui_api.confirm_coinjoin(
max_rounds=str(max_rounds),
max_feerate=max_fee_per_vbyte,
+ max_coordinator_fee_pct=max_coordinator_fee_pct,
) as layout:
return await raise_if_not_confirmed(layout, "coinjoin_final", BR_CODE_OTHER)
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index a8f63f4a..791a8fc2 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -2059,10 +2059,13 @@ async def confirm_modify_fee(
)
-async def confirm_coinjoin(max_rounds: int, max_fee_per_vbyte: str) -> None:
+async def confirm_coinjoin(
+ max_rounds: int, max_fee_per_vbyte: str, max_coordinator_fee_pct: str
+) -> None:
with trezorui_api.confirm_coinjoin(
max_rounds=str(max_rounds),
max_feerate=max_fee_per_vbyte,
+ max_coordinator_fee_pct=max_coordinator_fee_pct,
) as layout:
return await raise_if_not_confirmed(layout, "coinjoin_final", BR_CODE_OTHER)
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 5a148e16..f088e267 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -2165,10 +2165,13 @@ async def confirm_modify_fee(
)
-async def confirm_coinjoin(max_rounds: int, max_fee_per_vbyte: str) -> None:
+async def confirm_coinjoin(
+ max_rounds: int, max_fee_per_vbyte: str, max_coordinator_fee_pct: str
+) -> None:
with trezorui_api.confirm_coinjoin(
max_rounds=str(max_rounds),
max_feerate=max_fee_per_vbyte,
+ max_coordinator_fee_pct=max_coordinator_fee_pct,
) as layout:
return await raise_if_not_confirmed(layout, "coinjoin_final", BR_CODE_OTHER)
diff --git a/core/translations/en.json b/core/translations/en.json
index ab3283cc..35d25bea 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -536,6 +536,7 @@
"cardano__x_of_y_signatures_template": "Requires {0} out of {1} signatures.",
"coinjoin__access_account": "Access your coinjoin account?",
"coinjoin__do_not_disconnect": "Do not disconnect your Trezor!",
+ "coinjoin__max_coordinator_fee_pct": "Max coordinator fee",
"coinjoin__max_mining_fee": "Max mining fee",
"coinjoin__max_rounds": "Max rounds",
"coinjoin__title": "Authorize coinjoin",
diff --git a/core/translations/order.json b/core/translations/order.json
index d01e9486..234a1062 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1239,26 +1239,27 @@
"1237": "address__warning_not_yours",
"1238": "tron__owner_address",
"1239": "ethereum__eip_712_empty_domain",
- "1240": "ethereum__all_evms",
- "1241": "ethereum__auth_done",
- "1242": "ethereum__auth_title",
- "1243": "ethereum__auth_warn",
- "1244": "ethereum__calldata_digest",
- "1245": "ethereum__delegate_done",
- "1246": "ethereum__delegate_title",
- "1247": "ethereum__delegate_warn",
- "1248": "ethereum__delegating",
- "1249": "ethereum__network",
- "1250": "ethereum__revoke_done",
- "1251": "ethereum__revoke_title",
- "1252": "ethereum__revoke_warn",
- "1253": "ethereum__smart_info",
- "1254": "ethereum__to",
- "1255": "pin__reenter_new_description",
- "1256": "stellar__ext_auth",
- "1257": "stellar__ext_auth_message",
- "1258": "stellar__invoke_contract",
- "1259": "words__arguments",
- "1260": "words__authorization",
- "1261": "words__function"
+ "1240": "coinjoin__max_coordinator_fee_pct",
+ "1241": "ethereum__all_evms",
+ "1242": "ethereum__auth_done",
+ "1243": "ethereum__auth_title",
+ "1244": "ethereum__auth_warn",
+ "1245": "ethereum__calldata_digest",
+ "1246": "ethereum__delegate_done",
+ "1247": "ethereum__delegate_title",
+ "1248": "ethereum__delegate_warn",
+ "1249": "ethereum__delegating",
+ "1250": "ethereum__network",
+ "1251": "ethereum__revoke_done",
+ "1252": "ethereum__revoke_title",
+ "1253": "ethereum__revoke_warn",
+ "1254": "ethereum__smart_info",
+ "1255": "ethereum__to",
+ "1256": "pin__reenter_new_description",
+ "1257": "stellar__ext_auth",
+ "1258": "stellar__ext_auth_message",
+ "1259": "stellar__invoke_contract",
+ "1260": "words__arguments",
+ "1261": "words__authorization",
+ "1262": "words__function"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 1f9ab3fc..817ecd1e 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "22aafe9516200002c46444bb5e0705a362eb32793aed6e8b530ea0592fa534d0",
- "datetime": "2026-07-23T16:07:56.601237+00:00",
- "commit": "40244ab7c769b4390a35df2ee8fd714a7a1b7cbf"
+ "merkle_root": "0d386f66bc1fa1ed3fbc120142e2ddb1d7a577101f29bcc77c36dfef4637f7be",
+ "datetime": "2026-07-23T16:08:37.643036+00:00",
+ "commit": "9e9f21a1346b5314faaae3cfb537b40ea1a9f3f3"
},
"history": [
{
Why this scored 20/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.