Skip to content

feat(trezor-client): implement EIP-712 streaming typed data signing#7038

Open
ifdario wants to merge 2 commits into
trezor:mainfrom
infinitefield:feat/eip712-streaming-protocol
Open

feat(trezor-client): implement EIP-712 streaming typed data signing#7038
ifdario wants to merge 2 commits into
trezor:mainfrom
infinitefield:feat/eip712-streaming-protocol

Conversation

@ifdario
Copy link
Copy Markdown

@ifdario ifdario commented Jun 1, 2026

Summary

  • Add ethereum_sign_typed_data() implementing the full EthereumSignTypedData streaming protocol (message type 464)
  • Add ethereum_sign_typed_hash() for Model One backward compatibility (message type 470)
  • Add serde_json as optional dependency gated behind the ethereum feature
  • Handle uint256 and other large integer types correctly (up to 32 bytes)

Closes #7037

Context

The Rust trezor-client has protobuf bindings for the EIP-712 streaming protocol but no implementation to drive it. Modern Trezor devices (Safe 3, Safe 5, Model T) require this protocol and reject the legacy EthereumSignTypedHash with Failure_UnexpectedMessage.

The implementation handles the full streaming conversation:

  1. Send EthereumSignTypedData with primary type
  2. Respond to EthereumTypedDataStructRequest with type definitions
  3. Respond to EthereumTypedDataValueRequest with encoded values
  4. Handle ButtonRequest and PassphraseRequest during the flow
  5. Return signature from EthereumTypedDataSignature

Test plan

  • Tested with Trezor Safe 3 signing EIP-712 multisig transactions on Hyperliquid mainnet
  • Verified correct domain separator hash computation via streaming protocol
  • Verified passphrase handling during signing flow

Add ethereum_sign_typed_data() which implements the full
EthereumSignTypedData streaming protocol (message type 464).
This enables on-device EIP-712 signing on modern Trezor devices
(Safe 3, Safe 5, Model T) which do not support the legacy
EthereumSignTypedHash message.

Also adds ethereum_sign_typed_hash() for Model One compatibility.
@ifdario ifdario requested a review from obrusvit as a code owner June 1, 2026 08:55
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cdc1cc10-0207-4044-921b-a32cc7b7031e

📥 Commits

Reviewing files that changed from the base of the PR and between 4350074 and a3bd877.

📒 Files selected for processing (1)
  • rust/trezor-client/src/client/ethereum.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust/trezor-client/src/client/ethereum.rs

Walkthrough

This PR adds EIP-712 typed-data signing support to the Rust Trezor client. It introduces two public methods on the Trezor client: ethereum_sign_typed_hash for signing a precomputed domain separator (with optional message hash) and ethereum_sign_typed_data for the full streaming EIP-712 protocol. The implementation includes helpers to map EIP-712 type strings to protobuf field descriptors and to encode JSON values (integers, signed ints, addresses, bytes, booleans, strings, arrays) into the binary format expected by the device. The Cargo feature enables serde_json for these APIs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and accurately reflects the main change: implementing EIP-712 streaming typed data signing for the trezor-client.
Description check ✅ Passed The PR description is comprehensive, covering summary, context, implementation details, and test plan. It follows the repository's template structure with clear sections.
Linked Issues check ✅ Passed The PR fully addresses issue #7037 by implementing the EthereumSignTypedData streaming protocol with both the modern streaming method and Model One backward compatibility, plus proper large integer handling.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing EIP-712 streaming protocol: adding the required dependencies, new signing methods, and supporting helper functions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/trezor-client/src/client/ethereum.rs`:
- Around line 235-251: The PinMatrixRequest and PassphraseRequest handling must
route through the module's existing interaction/callback path instead of
returning UnsupportedNetwork or auto-answering with an empty passphrase: for
protos::MessageType::MessageType_PinMatrixRequest, parse the
protos::PinMatrixRequest via resp.into_message() and invoke the same interaction
callback used by other flows to collect the PIN from the host (or wait for
on-device entry), then send the collected PIN back with the appropriate
PinMatrixAck via self.call_raw; for
protos::MessageType::MessageType_PassphraseRequest, parse the
protos::PassphraseRequest via resp.into_message(), if pr._on_device() follow the
current on-device ack path (set_on_device(true) and call_raw), otherwise call
the shared interaction callback to obtain the host-entered passphrase and set it
on protos::PassphraseAck with set_passphrase(...) before calling self.call_raw;
ensure you reuse the exact callback/interaction machinery used for ButtonRequest
handling rather than hardcoding responses.
- Around line 379-425: The encoding functions currently silently drop or panic
on invalid/oversized inputs; change encode_uint_be, encode_int_be, and
encode_typed_value to return Result<Vec<u8>, Error> (or a suitable error type)
and validate inputs: for hex inputs in encode_uint_be/encode_int_be use
hex::decode? and propagate decode errors instead of unwrap_or_default, check
decoded.len() <= 32 and <= byte_len before copying to avoid
buf[start..start+len] panics, and return an Err on oversize; for decimal
parsing, reject values that don't fit the expected bit width (use
parse_type_size(type_name) to compute byte_len and ensure parsed u128/i128 or
bigint fits) rather than falling back to zero; update callers to propagate
errors. Ensure encode_typed_value also handles serde_json::Number by converting
safely (or using BigInt parsing) and returns Err on out-of-range inputs or
malformed hex.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52f9fab4-1c1f-4bb1-9c95-7b0ca5a64dac

📥 Commits

Reviewing files that changed from the base of the PR and between a3bc309 and 4350074.

⛔ Files ignored due to path filters (1)
  • rust/trezor-client/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • rust/trezor-client/Cargo.toml
  • rust/trezor-client/src/client/ethereum.rs

Comment thread rust/trezor-client/src/client/ethereum.rs Outdated
Comment thread rust/trezor-client/src/client/ethereum.rs
…tion handling

- Clamp byte_len to 32 and hex input length to prevent panics
- Handle hex::decode errors gracefully instead of unwrap_or_default
- Document that PIN/passphrase handling matches handle_interaction()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rust trezor_client is missing EthereumSignTypedData streaming protocol implementation

1 participant