#![allow(clippy::upper_case_acronyms)]
use std::fmt;
use thiserror::Error;
use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress, U256};
use trezor_client::client::AccessListItem as Trezor_AccessListItem;
#[derive(Clone, Debug)]
pub enum DerivationType {
TrezorLive(usize),
Other(String),
}
impl fmt::Display for DerivationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{}",
match self {
DerivationType::TrezorLive(index) => format!("m/44'/60'/{index}'/0/0"),
DerivationType::Other(inner) => inner.to_owned(),
}
)
}
}
#[derive(Error, Debug)]
pub enum TrezorError {
#[error(transparent)]
TrezorError(#[from] trezor_client::error::Error),
#[error("Trezor was not able to retrieve device features")]
FeaturesError,
#[error("Not able to unpack value for TrezorTransaction.")]
DataError,
#[error(transparent)]
HexError(#[from] hex::FromHexError),
#[error(transparent)]
SemVerError(#[from] semver::Error),
#[error("Trezor ethereum app requires at least version: {0:?}")]
UnsupportedFirmwareVersion(String),
#[error("Does not support ENS.")]
NoENSSupport,
#[error("Unable to access trezor cached session.")]
CacheError(String),
}
pub struct TrezorTransaction {
pub nonce: Vec<u8>,
pub gas: Vec<u8>,
pub gas_price: Vec<u8>,
pub value: Vec<u8>,
pub to: String,
pub data: Vec<u8>,
pub max_fee_per_gas: Vec<u8>,
pub max_priority_fee_per_gas: Vec<u8>,
pub access_list: Vec<Trezor_AccessListItem>,
}
impl TrezorTransaction {
fn to_trimmed_big_endian(_value: &U256) -> Vec<u8> {
let mut trimmed_value = [0_u8; 32];
_value.to_big_endian(&mut trimmed_value);
trimmed_value[_value.leading_zeros() as usize / 8..].to_vec()
}
pub fn load(tx: &TypedTransaction) -> Result<Self, TrezorError> {
let to: String = match tx.to() {
Some(v) => match v {
NameOrAddress::Name(_) => return Err(TrezorError::NoENSSupport),
NameOrAddress::Address(value) => hex::encode_prefixed(value),
},
None => "".to_string(),
};
let nonce = tx.nonce().map_or(vec![], Self::to_trimmed_big_endian);
let gas = tx.gas().map_or(vec![], Self::to_trimmed_big_endian);
let gas_price = tx.gas_price().map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
let value = tx.value().map_or(vec![], Self::to_trimmed_big_endian);
let data = tx.data().map_or(vec![], |v| v.to_vec());
match tx {
TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => Ok(Self {
nonce,
gas,
gas_price,
value,
to,
data,
max_fee_per_gas: vec![],
max_priority_fee_per_gas: vec![],
access_list: vec![],
}),
TypedTransaction::Eip1559(eip1559_tx) => {
let max_fee_per_gas =
eip1559_tx.max_fee_per_gas.map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
let max_priority_fee_per_gas = eip1559_tx
.max_priority_fee_per_gas
.map_or(vec![], |v| Self::to_trimmed_big_endian(&v));
let mut access_list: Vec<Trezor_AccessListItem> = Vec::new();
for item in &eip1559_tx.access_list.0 {
let address: String = hex::encode_prefixed(item.address);
let mut storage_keys: Vec<Vec<u8>> = Vec::new();
for key in &item.storage_keys {
storage_keys.push(key.as_bytes().to_vec())
}
access_list.push(Trezor_AccessListItem { address, storage_keys })
}
Ok(Self {
nonce,
gas,
gas_price,
value,
to,
data,
max_fee_per_gas,
max_priority_fee_per_gas,
access_list,
})
}
#[cfg(feature = "optimism")]
TypedTransaction::DepositTransaction(_) => Ok(Self {
nonce,
gas,
gas_price,
value,
to,
data,
max_fee_per_gas: vec![],
max_priority_fee_per_gas: vec![],
access_list: vec![],
}),
}
}
}