@@ -64,11 +64,49 @@ static void eip7251_plugin_provider_parameter(ethPluginProvideParameter_t *param
6464 }
6565}
6666
67+ // EIP-7251 charges a dynamic per-request fee paid in native value. In normal
68+ // operation this fee is in the wei-to-gwei range — showing a "Tx value: 1 wei"
69+ // screen on every legitimate request is noisy without informing the user.
70+ // Hide the screen as long as the value stays below this threshold; anything
71+ // above is worth surfacing because it dwarfs the protocol fee. The attacker
72+ // budget for a hidden value is therefore capped at TX_VALUE_MIN_DISPLAY_WEI,
73+ // i.e. dust ($0.000004 at $4000/ETH), instead of the unbounded original CVE.
74+ #define TX_VALUE_MIN_DISPLAY_WEI 1000000000ULL // 1 gwei
75+
76+ // Whether the transaction carries a native value worth surfacing to the user.
77+ // NULL-safe so callers can pass param->txContent directly from either the
78+ // ETH_PLUGIN_FINALIZE or ETH_PLUGIN_QUERY_CONTRACT_UI message structs.
79+ static bool has_tx_value (const txContent_t * txContent ) {
80+ if ((txContent == NULL ) || (txContent -> value .length == 0 )) {
81+ return false;
82+ }
83+ if (txContent -> value .length > sizeof (uint64_t )) {
84+ // > 2^64 wei is unambiguously larger than the threshold.
85+ return true;
86+ }
87+ uint64_t val = 0 ;
88+ for (uint8_t i = 0 ; i < txContent -> value .length ; ++ i ) {
89+ val = (val << 8 ) | txContent -> value .value [i ];
90+ }
91+ return val > TX_VALUE_MIN_DISPLAY_WEI ;
92+ }
93+
6794static void eip7251_plugin_finalize (ethPluginFinalize_t * param ) {
6895 eip7251_context_t * context = (eip7251_context_t * ) param -> pluginContext ;
6996
7097 param -> uiType = ETH_UI_TYPE_GENERIC ;
71- param -> numScreens = target_equals_source (context ) ? 1 : 2 ;
98+ // Source validator is always shown. Target is shown when distinct. The
99+ // native tx.value is shown only when it exceeds the dust threshold
100+ // defined by has_tx_value, so a hostile dApp cannot smuggle a meaningful
101+ // ETH/native value into a consolidation request that otherwise only
102+ // renders validator pubkeys.
103+ param -> numScreens = 1 ;
104+ if (!target_equals_source (context )) {
105+ param -> numScreens ++ ;
106+ }
107+ if (has_tx_value (param -> txContent )) {
108+ param -> numScreens ++ ;
109+ }
72110 param -> result = (context -> received == sizeof (context -> consolidation_request ))
73111 ? ETH_PLUGIN_RESULT_OK
74112 : ETH_PLUGIN_RESULT_ERROR ;
@@ -86,33 +124,60 @@ static void eip7251_plugin_query_contract_id(ethQueryContractID_t *param) {
86124
87125static void eip7251_plugin_query_contract_ui (ethQueryContractUI_t * param ) {
88126 eip7251_context_t * context = (eip7251_context_t * ) param -> pluginContext ;
127+ // Map a screen index to a logical screen kind based on which optional
128+ // screens are present for this transaction.
129+ bool show_target = !target_equals_source (context );
130+ bool show_tx_value = has_tx_value (param -> txContent );
131+ uint8_t idx = param -> screenIndex ;
132+ enum { S_SOURCE , S_TARGET , S_TX_VALUE , S_UNKNOWN } screen = S_UNKNOWN ;
133+
134+ if (idx == 0 ) {
135+ screen = S_SOURCE ;
136+ } else if (show_target && idx == 1 ) {
137+ screen = S_TARGET ;
138+ } else if (show_tx_value && idx == (show_target ? 2 : 1 )) {
139+ screen = S_TX_VALUE ;
140+ }
89141
90- if (param -> msgLength >= 2 ) {
91- memcpy (param -> msg , "0x" , 2 );
92- switch (param -> screenIndex ) {
93- case 0 :
94- if (target_equals_source (context )) {
95- strlcpy (param -> title , "Validator" , param -> titleLength );
96- } else {
97- strlcpy (param -> title , "From validator" , param -> titleLength );
98- }
99- format_hex (context -> source_pubkey ,
100- sizeof (context -> source_pubkey ),
101- & param -> msg [2 ],
102- param -> msgLength - 2 );
103- break ;
104- case 1 :
105- strlcpy (param -> title , "To validator" , param -> titleLength );
106- format_hex (context -> target_pubkey ,
107- sizeof (context -> target_pubkey ),
108- & param -> msg [2 ],
109- param -> msgLength - 2 );
110- break ;
111- default :
112- break ;
142+ if (param -> msgLength < 2 ) {
143+ return ;
144+ }
145+ switch (screen ) {
146+ case S_SOURCE :
147+ memcpy (param -> msg , "0x" , 2 );
148+ strlcpy (param -> title , show_target ? "From validator" : "Validator" , param -> titleLength );
149+ format_hex (context -> source_pubkey ,
150+ sizeof (context -> source_pubkey ),
151+ & param -> msg [2 ],
152+ param -> msgLength - 2 );
153+ break ;
154+ case S_TARGET :
155+ memcpy (param -> msg , "0x" , 2 );
156+ strlcpy (param -> title , "To validator" , param -> titleLength );
157+ format_hex (context -> target_pubkey ,
158+ sizeof (context -> target_pubkey ),
159+ & param -> msg [2 ],
160+ param -> msgLength - 2 );
161+ break ;
162+ case S_TX_VALUE : {
163+ uint64_t chain_id = get_tx_chain_id ();
164+ const char * ticker = get_displayable_ticker (& chain_id , g_chain_config , true);
165+ strlcpy (param -> title , "Tx value" , param -> titleLength );
166+ if (!amountToString (param -> txContent -> value .value ,
167+ param -> txContent -> value .length ,
168+ WEI_TO_ETHER ,
169+ ticker ,
170+ param -> msg ,
171+ param -> msgLength )) {
172+ param -> result = ETH_PLUGIN_RESULT_ERROR ;
173+ return ;
174+ }
175+ break ;
113176 }
114- param -> result = ETH_PLUGIN_RESULT_OK ;
177+ case S_UNKNOWN :
178+ return ;
115179 }
180+ param -> result = ETH_PLUGIN_RESULT_OK ;
116181}
117182
118183void eip7251_plugin_call (eth_plugin_msg_t msg , void * param ) {
0 commit comments