test(ethereum): check function signature at source.
What changed, and why it matters
This is a test-only refactoring commit. It changes how Ethereum function signatures are represented in firmware source code (from hex strings converted at runtime to raw byte literals) and updates the corresponding unit test to verify those signatures against Solidity function definitions. There is no runtime behavior change and no security fix or vulnerability.
No security action required. Treat as normal code-quality/test refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors clear_signing_definitions.py to replace ubinascii.unhexlify() calls with equivalent raw Python bytes literals for contract addresses and function selectors. It also rewrites the test file to import function selectors directly from source and validate them by recomputing keccak256(signature)[:4], rather than maintaining a separate hardcoded table of hex selectors. This is a maintainability/test-coverage improvement, not a functional or security patch.
Changed components
core/src/apps/ethereum/clear_signing_definitions.pycore/tests/test_apps.ethereum.clear_signing_definitions.pyInspect captured patch +135 / −127
diff --git a/core/src/apps/ethereum/clear_signing_definitions.py b/core/src/apps/ethereum/clear_signing_definitions.py
index dc80bc58..2c4c5b99 100644
--- a/core/src/apps/ethereum/clear_signing_definitions.py
+++ b/core/src/apps/ethereum/clear_signing_definitions.py
@@ -53,8 +53,6 @@ TRANSFER_DISPLAY_FORMAT = DisplayFormat(
def all_display_formats() -> Generator[DisplayFormat, None, None]:
- from ubinascii import unhexlify
-
from .clear_signing import (
AmountFormatter,
Array,
@@ -76,7 +74,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
yield TRANSFER_DISPLAY_FORMAT
# https://github.com/LedgerHQ/clear-signing-erc7730-registry/blob/master/registry/1inch/calldata-AggregationRouterV6.json#L9
- ONEINCH_ADDRESS = unhexlify("111111125421cA6dc452d289314280a0f8842A65")
+ ONEINCH_ADDRESS = b"\x11\x11\x11\x12\x54\x21\xca\x6d\xc4\x52\xd2\x89\x31\x42\x80\xa0\xf8\x84\x2a\x65"
ONEINCH_CHAINS = [
1,
10,
@@ -95,8 +93,8 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# $.metadata.constants.addressAsEth and addressAsNull from common-AggregationRouterV6.json
ONEINCH_NATIVE_CURRENCY_ADDRESSES = [
- unhexlify("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"),
- unhexlify("0000000000000000000000000000000000000000"),
+ b"\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee",
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
]
ONEINCH_CONTEXT = BindingContext(
@@ -113,7 +111,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# omit the field rather than display a bare, tokenless integer. (`swap` keeps
# its minReturnAmount because its dstToken is available.)
- _FUNC_SIG = unhexlify("07ed2379")
+ _FUNC_SIG = b"\x07\xed\x23\x79"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -159,7 +157,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("83800a8e")
+ _FUNC_SIG = b"\x83\x80\x0a\x8e"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -194,7 +192,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("e2c95c82")
+ _FUNC_SIG = b"\xe2\xc9\x5c\x82"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -230,7 +228,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("8770ba91")
+ _FUNC_SIG = b"\x87\x70\xba\x91"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -266,7 +264,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("19367472")
+ _FUNC_SIG = b"\x19\x36\x74\x72"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -303,7 +301,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("ea76dddf")
+ _FUNC_SIG = b"\xea\x76\xdd\xdf"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -340,7 +338,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("f7a70056")
+ _FUNC_SIG = b"\xf7\xa7\x00\x56"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -378,7 +376,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("a76dfc3b")
+ _FUNC_SIG = b"\xa7\x6d\xfc\x3b"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -408,7 +406,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("89af926a")
+ _FUNC_SIG = b"\x89\xaf\x92\x6a"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -439,7 +437,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("188ac35d")
+ _FUNC_SIG = b"\x18\x8a\xc3\x5d"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -471,7 +469,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("175accdc")
+ _FUNC_SIG = b"\x17\x5a\xcc\xdc"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -502,7 +500,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("0f449d71")
+ _FUNC_SIG = b"\x0f\x44\x9d\x71"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -534,7 +532,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("493189f0")
+ _FUNC_SIG = b"\x49\x31\x89\xf0"
yield (
DisplayFormat(
binding_context=ONEINCH_CONTEXT,
@@ -568,7 +566,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
# https://github.com/LedgerHQ/clear-signing-erc7730-registry/blob/master/registry/lifi/calldata-LIFIDiamond.json
- LIFI_ADDRESS = unhexlify("1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE")
+ LIFI_ADDRESS = b"\x12\x31\xde\xb6\xf5\x74\x9e\xf6\xce\x69\x43\xa2\x75\xa1\xd3\xe7\x48\x6f\x4e\xae"
# Chains where the LiFi diamond is deployed at the canonical LIFI_ADDRESS.
LIFI_CHAINS = [
1,
@@ -600,10 +598,22 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
]
# Chains where the LiFi diamond is deployed at a non-canonical address.
LIFI_ALT_DEPLOYMENTS = [
- (324, unhexlify("341e94069f53234fe6dabef707ad424830525715")), # zkSync Era
- (1088, unhexlify("24ca98fb6972f5ee05f0db00595c7f68d9fafd68")), # Metis
- (59144, unhexlify("de1e598b81620773454588b85d6b5d4eec32573e")), # Linea
- (167004, unhexlify("3a9a5dba8fe1c4da98187ce4755701bca182f63b")),
+ (
+ 324,
+ b"\x34\x1e\x94\x06\x9f\x53\x23\x4f\xe6\xda\xbe\xf7\x07\xad\x42\x48\x30\x52\x57\x15",
+ ), # zkSync Era
+ (
+ 1088,
+ b"\x24\xca\x98\xfb\x69\x72\xf5\xee\x05\xf0\xdb\x00\x59\x5c\x7f\x68\xd9\xfa\xfd\x68",
+ ), # Metis
+ (
+ 59144,
+ b"\xde\x1e\x59\x8b\x81\x62\x07\x73\x45\x45\x88\xb8\x5d\x6b\x5d\x4e\xec\x32\x57\x3e",
+ ), # Linea
+ (
+ 167004,
+ b"\x3a\x9a\x5d\xba\x8f\xe1\xc4\xda\x98\x18\x7c\xe4\x75\x57\x01\xbc\xa1\x82\xf6\x3b",
+ ),
]
LIFI_CONTEXT = BindingContext(
@@ -611,11 +621,11 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
LIFI_NATIVE_CURRENCY_ADDRESSES = [
- unhexlify("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"),
- unhexlify("0000000000000000000000000000000000000000"),
+ b"\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee",
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
]
- _FUNC_SIG = unhexlify("5fd9ae2e")
+ _FUNC_SIG = b"\x5f\xd9\xae\x2e"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -666,7 +676,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("2c57e884")
+ _FUNC_SIG = b"\x2c\x57\xe8\x84"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -715,7 +725,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("736eac0b")
+ _FUNC_SIG = b"\x73\x6e\xac\x0b"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -764,7 +774,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("4666fc80")
+ _FUNC_SIG = b"\x46\x66\xfc\x80"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -811,7 +821,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("733214a3")
+ _FUNC_SIG = b"\x73\x32\x14\xa3"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -858,7 +868,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("af7060fd")
+ _FUNC_SIG = b"\xaf\x70\x60\xfd"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -905,7 +915,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("4630a0d8")
+ _FUNC_SIG = b"\x46\x30\xa0\xd8"
yield (
DisplayFormat(
binding_context=LIFI_CONTEXT,
@@ -963,7 +973,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
# https://github.com/LedgerHQ/clear-signing-erc7730-registry/blob/master/registry/uniswap/calldata-UniswapV3Router02.json#L6
- UNISWAP_V3_ROUTER_ADDRESS = unhexlify("68b3465833fb72A70ecDF485E0e4C7bD8665Fc45")
+ UNISWAP_V3_ROUTER_ADDRESS = b"\x68\xb3\x46\x58\x33\xfb\x72\xa7\x0e\xcd\xf4\x85\xe0\xe4\xc7\xbd\x86\x65\xfc\x45"
UNISWAP_V3_ROUTER_CHAINS = [1]
# https://github.com/LedgerHQ/clear-signing-erc7730-registry/blob/master/registry/uniswap/calldata-UniswapV3Router02.json
@@ -972,7 +982,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
[(chain, UNISWAP_V3_ROUTER_ADDRESS) for chain in UNISWAP_V3_ROUTER_CHAINS],
)
- _FUNC_SIG = unhexlify("b858183f")
+ _FUNC_SIG = b"\xb8\x58\x18\x3f"
yield (
DisplayFormat(
binding_context=UNISWAP_CONTEXT,
@@ -1013,7 +1023,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("04e45aaf")
+ _FUNC_SIG = b"\x04\xe4\x5a\xaf"
yield (
DisplayFormat(
binding_context=UNISWAP_CONTEXT,
@@ -1062,7 +1072,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("09b81346")
+ _FUNC_SIG = b"\x09\xb8\x13\x46"
yield (
DisplayFormat(
binding_context=UNISWAP_CONTEXT,
@@ -1103,7 +1113,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
)
)
- _FUNC_SIG = unhexlify("5023b4df")
+ _FUNC_SIG = b"\x50\x23\xb4\xdf"
yield (
DisplayFormat(
binding_context=UNISWAP_CONTEXT,
@@ -1160,9 +1170,9 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# * TREZOR_TEST_ARRAYS_DESCRIPTOR - multi-value arrays
# * TREZOR_TEST_PATHS_DESCRIPTOR - composite path styles (slices + nested)
TREZOR_TEST_CHAIN_ID = 1
- TREZOR_TEST_ADDRESS = unhexlify("dddddddddddddddddddddddddddddddddddddddd")
- TREZOR_TEST_CONST_TOKEN = unhexlify("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
- TREZOR_TEST_NATIVE = unhexlify("0000000000000000000000000000000000000000")
+ TREZOR_TEST_ADDRESS = b"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ TREZOR_TEST_CONST_TOKEN = b"\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee"
+ TREZOR_TEST_NATIVE = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
TREZOR_TEST_CONTEXT = BindingContext(
[(TREZOR_TEST_CHAIN_ID, TREZOR_TEST_ADDRESS)]
@@ -1171,7 +1181,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# --- 1) scalar / atomic formatters ---
yield DisplayFormat(
binding_context=TREZOR_TEST_CONTEXT,
- func_sig=unhexlify("7e577e01"), # synthetic selector (dummy contract)
+ func_sig=b"\x7e\x57\x7e\x01", # synthetic selector (dummy contract)
intent="Trezor Test Scalars. DO NOT USE",
parameter_definitions=[
Atomic(parse_address), # 0 recipient
@@ -1206,7 +1216,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# --- 2) token-amount resolution: via token_path and via constant address ---
yield DisplayFormat(
binding_context=TREZOR_TEST_CONTEXT,
- func_sig=unhexlify("7e577e02"), # synthetic selector (dummy contract)
+ func_sig=b"\x7e\x57\x7e\x02", # synthetic selector (dummy contract)
intent="Trezor Test Token. DO NOT USE",
parameter_definitions=[
Atomic(parse_address), # 0 token (target of token_path below)
@@ -1228,7 +1238,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# --- 3) multi-value arrays ---
yield DisplayFormat(
binding_context=TREZOR_TEST_CONTEXT,
- func_sig=unhexlify("7e577e03"), # synthetic selector (dummy contract)
+ func_sig=b"\x7e\x57\x7e\x03", # synthetic selector (dummy contract)
intent="Trezor Test Arrays. DO NOT USE",
parameter_definitions=[
Array(Atomic(parse_uint256)), # 0 amounts (multi-value array)
@@ -1256,7 +1266,7 @@ def all_display_formats() -> Generator[DisplayFormat, None, None]:
# --- 4) composite path styles: bytes slicing + nested array-of-structs ---
yield DisplayFormat(
binding_context=TREZOR_TEST_CONTEXT,
- func_sig=unhexlify("7e577e04"), # synthetic selector (dummy contract)
+ func_sig=b"\x7e\x57\x7e\x04", # synthetic selector (dummy contract)
intent="Trezor Test Paths. DO NOT USE",
parameter_definitions=[
Atomic(parse_uint256), # 0 amount (reused by both slice fields)
diff --git a/core/tests/test_apps.ethereum.clear_signing_definitions.py b/core/tests/test_apps.ethereum.clear_signing_definitions.py
index 011dde61..d206cf40 100644
--- a/core/tests/test_apps.ethereum.clear_signing_definitions.py
+++ b/core/tests/test_apps.ethereum.clear_signing_definitions.py
@@ -2,99 +2,97 @@
from common import * # isort:skip
if not utils.BITCOIN_ONLY:
- from trezor.crypto import base58
+ from trezor.crypto.hashlib import sha3_256
- from apps.ethereum.clear_signing_definitions import (
- APPROVE_DISPLAY_FORMAT,
- TRANSFER_DISPLAY_FORMAT,
+ from apps.ethereum.clear_signing_definitions import all_display_formats
+ from apps.ethereum.yielding import (
+ CLAIM_DISPLAY_FORMAT,
+ DEPOSIT_DISPLAY_FORMAT,
+ REDEEM_DISPLAY_FORMAT,
+ WITHDRAW_DISPLAY_FORMAT,
)
-# (hex selector, Solidity function signature) pairs — each hardcoded selector
-# must match the first 4 bytes of keccak256(signature).
-_KNOWN_FUNC_SIGS = (
- ("095ea7b3", b"approve(address,uint256)"),
- ("a9059cbb", b"transfer(address,uint256)"),
- (
- "07ed2379",
- b"swap(address,(address,address,address,address,uint256,uint256,uint256),bytes)",
- ),
- ("83800a8e", b"unoswap(uint256,uint256,uint256,uint256)"),
- ("e2c95c82", b"unoswapTo(uint256,uint256,uint256,uint256,uint256)"),
- ("8770ba91", b"unoswap2(uint256,uint256,uint256,uint256,uint256)"),
- ("19367472", b"unoswap3(uint256,uint256,uint256,uint256,uint256,uint256)"),
- ("ea76dddf", b"unoswapTo2(uint256,uint256,uint256,uint256,uint256,uint256)"),
- (
- "f7a70056",
- b"unoswapTo3(uint256,uint256,uint256,uint256,uint256,uint256,uint256)",
- ),
- ("a76dfc3b", b"ethUnoswap(uint256,uint256)"),
- ("89af926a", b"ethUnoswap2(uint256,uint256,uint256)"),
- ("188ac35d", b"ethUnoswap3(uint256,uint256,uint256,uint256)"),
- ("175accdc", b"ethUnoswapTo(uint256,uint256,uint256)"),
- ("0f449d71", b"ethUnoswapTo2(uint256,uint256,uint256,uint256)"),
- ("493189f0", b"ethUnoswapTo3(uint256,uint256,uint256,uint256,uint256)"),
- (
- "5fd9ae2e",
- b"swapTokensMultipleV3ERC20ToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
- ),
- (
- "2c57e884",
- b"swapTokensMultipleV3ERC20ToNative(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
- ),
- (
- "736eac0b",
- b"swapTokensMultipleV3NativeToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
- ),
- (
- "4666fc80",
- b"swapTokensSingleV3ERC20ToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
- ),
- (
- "733214a3",
- b"swapTokensSingleV3ERC20ToNative(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
- ),
- (
- "af7060fd",
- b"swapTokensSingleV3NativeToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
- ),
- (
- "4630a0d8",
- b"swapTokensGeneric(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
- ),
- ("b858183f", b"exactInput((bytes,address,uint256,uint256))"),
- (
- "04e45aaf",
- b"exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))",
- ),
- ("09b81346", b"exactOutput((bytes,address,uint256,uint256))"),
- (
- "5023b4df",
- b"exactOutputSingle((address,address,uint24,address,uint256,uint256,uint160))",
- ),
+# Human-readable Solidity signatures for every function selector embedded in
+# the firmware. The test recomputes keccak256(signature)[:4] for each entry
+# and matches it against the binary `func_sig` actually stored in the shipped
+# DisplayFormat objects, so a typo in a firmware selector (or a stale entry
+# here) fails the test.
+_SOLIDITY_SIGNATURES = (
+ # ERC-20
+ b"approve(address,uint256)",
+ b"transfer(address,uint256)",
+ # 1inch AggregationRouterV6
+ b"swap(address,(address,address,address,address,uint256,uint256,uint256),bytes)",
+ b"unoswap(uint256,uint256,uint256,uint256)",
+ b"unoswapTo(uint256,uint256,uint256,uint256,uint256)",
+ b"unoswap2(uint256,uint256,uint256,uint256,uint256)",
+ b"unoswap3(uint256,uint256,uint256,uint256,uint256,uint256)",
+ b"unoswapTo2(uint256,uint256,uint256,uint256,uint256,uint256)",
+ b"unoswapTo3(uint256,uint256,uint256,uint256,uint256,uint256,uint256)",
+ b"ethUnoswap(uint256,uint256)",
+ b"ethUnoswap2(uint256,uint256,uint256)",
+ b"ethUnoswap3(uint256,uint256,uint256,uint256)",
+ b"ethUnoswapTo(uint256,uint256,uint256)",
+ b"ethUnoswapTo2(uint256,uint256,uint256,uint256)",
+ b"ethUnoswapTo3(uint256,uint256,uint256,uint256,uint256)",
+ # LiFi LIFIDiamond
+ b"swapTokensMultipleV3ERC20ToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
+ b"swapTokensMultipleV3ERC20ToNative(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
+ b"swapTokensMultipleV3NativeToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
+ b"swapTokensSingleV3ERC20ToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
+ b"swapTokensSingleV3ERC20ToNative(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
+ b"swapTokensSingleV3NativeToERC20(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool))",
+ b"swapTokensGeneric(bytes32,string,string,address,uint256,(address,address,address,address,uint256,bytes,bool)[])",
+ # Uniswap V3 Router 02
+ b"exactInput((bytes,address,uint256,uint256))",
+ b"exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))",
+ b"exactOutput((bytes,address,uint256,uint256))",
+ b"exactOutputSingle((address,address,uint24,address,uint256,uint256,uint160))",
+ # ERC-4626 vaults + Merkl claim (apps.ethereum.yielding)
+ b"deposit(uint256,address)",
+ b"withdraw(uint256,address,address)",
+ b"redeem(uint256,address,address)",
+ b"claim(address[],address[],uint256[],bytes32[][])",
)
+def _selector(signature: bytes) -> bytes:
+ return sha3_256(signature, keccak=True).digest()[:4]
+
+
+def _shipped_display_formats():
+ yield from all_display_formats()
+ yield DEPOSIT_DISPLAY_FORMAT
+ yield WITHDRAW_DISPLAY_FORMAT
+ yield REDEEM_DISPLAY_FORMAT
+ yield CLAIM_DISPLAY_FORMAT
+
+
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestEthereumClearSigningDefinitions(unittest.TestCase):
- def test_known_func_sigs(self):
- # Verify that every hardcoded function selector matches the first 4
- # bytes of keccak256 of its Solidity signature, so a typo cannot
- # silently match the wrong contract method.
- for hex_selector, signature in _KNOWN_FUNC_SIGS:
- self.assertEqual(
- unhexlify(hex_selector),
- base58.keccak_32(signature),
- msg=f"selector mismatch for {signature.decode()}",
+ def test_func_sigs_match_solidity_signatures(self):
+ expected = {_selector(sig): sig for sig in _SOLIDITY_SIGNATURES}
+ # no duplicate entries / selector collisions in the table above
+ self.assertEqual(len(expected), len(_SOLIDITY_SIGNATURES))
+
+ seen = set()
+ for display_format in _shipped_display_formats():
+ func_sig = bytes(display_format.func_sig)
+ if display_format.intent.startswith("Trezor Test"):
+ # synthetic selectors of the debug-only test contracts must
+ # not collide with any real function
+ self.assertFalse(func_sig in expected)
+ continue
+ self.assertIn(
+ func_sig,
+ expected,
+ msg=f"selector {hexlify(func_sig).decode()} (intent {display_format.intent}) matches no known Solidity signature",
)
+ seen.add(func_sig)
- def test_approve_transfer_func_sigs(self):
- self.assertEqual(
- APPROVE_DISPLAY_FORMAT.func_sig,
- base58.keccak_32(b"approve(address,uint256)"),
- )
- self.assertEqual(
- TRANSFER_DISPLAY_FORMAT.func_sig,
- base58.keccak_32(b"transfer(address,uint256)"),
+ stale = sorted(expected[sel].decode() for sel in set(expected) - seen)
+ self.assertFalse(
+ stale, msg=f"signatures not used by any DisplayFormat: {stale}"
)
Why this scored 15/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.