ethers_signers/trezor/
types.rs

1#![allow(clippy::upper_case_acronyms)]
2//! Helpers for interacting with the Ethereum Trezor App
3//! [Official Docs](https://github.com/TrezorHQ/app-ethereum/blob/master/doc/ethapp.asc)
4use std::fmt;
5use thiserror::Error;
6
7use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress, U256};
8use trezor_client::client::AccessListItem as Trezor_AccessListItem;
9
10#[derive(Clone, Debug)]
11/// Trezor wallet type
12pub enum DerivationType {
13    /// Trezor Live-generated HD path
14    TrezorLive(usize),
15    /// Any other path. Attention! Trezor by default forbids custom derivation paths
16    /// Run trezorctl set safety-checks prompt, to allow it
17    Other(String),
18}
19
20impl fmt::Display for DerivationType {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
22        write!(
23            f,
24            "{}",
25            match self {
26                DerivationType::TrezorLive(index) => format!("m/44'/60'/{index}'/0/0"),
27                DerivationType::Other(inner) => inner.to_owned(),
28            }
29        )
30    }
31}
32
33#[derive(Error, Debug)]
34/// Error when using the Trezor transport
35pub enum TrezorError {
36    /// Underlying Trezor transport error
37    #[error(transparent)]
38    TrezorError(#[from] trezor_client::error::Error),
39    #[error("Trezor was not able to retrieve device features")]
40    FeaturesError,
41    #[error("Not able to unpack value for TrezorTransaction.")]
42    DataError,
43    /// Error when converting from a hex string
44    #[error(transparent)]
45    HexError(#[from] hex::FromHexError),
46    /// Error when converting a semver requirement
47    #[error(transparent)]
48    SemVerError(#[from] semver::Error),
49    /// Error when signing EIP712 struct with not compatible Trezor ETH app
50    #[error("Trezor ethereum app requires at least version: {0:?}")]
51    UnsupportedFirmwareVersion(String),
52    #[error("Does not support ENS.")]
53    NoENSSupport,
54    #[error("Unable to access trezor cached session.")]
55    CacheError(String),
56}
57
58/// Trezor Transaction Struct
59pub struct TrezorTransaction {
60    pub nonce: Vec<u8>,
61    pub gas: Vec<u8>,
62    pub gas_price: Vec<u8>,
63    pub value: Vec<u8>,
64    pub to: String,
65    pub data: Vec<u8>,
66    pub max_fee_per_gas: Vec<u8>,
67    pub max_priority_fee_per_gas: Vec<u8>,
68    pub access_list: Vec<Trezor_AccessListItem>,
69}
70
71impl TrezorTransaction {
72    fn to_trimmed_big_endian(_value: &U256) -> Vec<u8> {
73        let mut trimmed_value = [0_u8; 32];
74        _value.to_big_endian(&mut trimmed_value);
75        trimmed_value[_value.leading_zeros() as usize / 8..].to_vec()
76    }
77
78    pub fn load(tx: &TypedTransaction) -> Result<Self, TrezorError> {
79        let to: String = match tx.to() {
80            Some(v) => match v {
81                NameOrAddress::Name(_) => return Err(TrezorError::NoENSSupport),
82                NameOrAddress::Address(value) => hex::encode_prefixed(value),
83            },
84            // Contract Creation
85            None => "".to_string(),
86        };
87
88        let nonce = tx.nonce().map_or(vec![], Self::to_trimmed_big_endian);
89        let gas = tx.gas().map_or(vec![], Self::to_trimmed_big_endian);
90        let gas_price = tx.gas_price().map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
91        let value = tx.value().map_or(vec![], Self::to_trimmed_big_endian);
92        let data = tx.data().map_or(vec![], |v| v.to_vec());
93
94        match tx {
95            TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => Ok(Self {
96                nonce,
97                gas,
98                gas_price,
99                value,
100                to,
101                data,
102                max_fee_per_gas: vec![],
103                max_priority_fee_per_gas: vec![],
104                access_list: vec![],
105            }),
106            TypedTransaction::Eip1559(eip1559_tx) => {
107                let max_fee_per_gas =
108                    eip1559_tx.max_fee_per_gas.map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
109
110                let max_priority_fee_per_gas = eip1559_tx
111                    .max_priority_fee_per_gas
112                    .map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
113
114                let mut access_list: Vec<Trezor_AccessListItem> = Vec::new();
115                for item in &eip1559_tx.access_list.0 {
116                    let address: String = hex::encode_prefixed(item.address);
117                    let mut storage_keys: Vec<Vec<u8>> = Vec::new();
118
119                    for key in &item.storage_keys {
120                        storage_keys.push(key.as_bytes().to_vec())
121                    }
122
123                    access_list.push(Trezor_AccessListItem { address, storage_keys })
124                }
125
126                Ok(Self {
127                    nonce,
128                    gas,
129                    gas_price,
130                    value,
131                    to,
132                    data,
133                    max_fee_per_gas,
134                    max_priority_fee_per_gas,
135                    access_list,
136                })
137            }
138            #[cfg(feature = "optimism")]
139            TypedTransaction::DepositTransaction(_) => Ok(Self {
140                nonce,
141                gas,
142                gas_price,
143                value,
144                to,
145                data,
146                max_fee_per_gas: vec![],
147                max_priority_fee_per_gas: vec![],
148                access_list: vec![],
149            }),
150        }
151    }
152}