ledger_ethereum/
lib.rs

1pub(crate) mod command;
2pub(crate) mod types;
3
4pub use command::get_address::*;
5pub use command::get_app_configuration::*;
6pub use command::provide_erc20_token_info::*;
7pub use command::sign_transaction::*;
8use ledger_transport::{APDUCommand, APDUErrorCode, Exchange};
9use ledger_zondax_generic::{App, LedgerAppError};
10pub use types::*;
11
12// https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp.adoc#general-purpose-apdus
13// https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/hw-app-eth/src/Eth.ts
14#[derive(Debug)]
15pub struct EthApp<E: Exchange> {
16    transport: E,
17}
18
19impl<E: Exchange> App for EthApp<E> {
20    const CLA: u8 = 0xe0;
21}
22
23impl<E: Exchange> EthApp<E> {
24    /// Create a new [`EthApp`] with the given transport
25    pub const fn new(transport: E) -> Self {
26        EthApp { transport }
27    }
28}
29
30impl<E> EthApp<E>
31where
32    E: Exchange + Send + Sync,
33    E::Error: std::error::Error,
34{
35    pub async fn send_chunks(
36        &self,
37        mut command: APDUCommand<Vec<u8>>,
38    ) -> Result<ledger_transport::APDUAnswer<E::AnswerType>, LedgerAppError<E::Error>> {
39        let chunks = command
40            .data
41            .chunks(250)
42            .map(|c| c.to_vec())
43            .collect::<Vec<Vec<u8>>>();
44        match chunks.len() {
45            0 => return Err(LedgerAppError::InvalidEmptyMessage),
46            n if n > 255 => return Err(LedgerAppError::InvalidMessageSize),
47            _ => (),
48        }
49
50        let (first, rest) = chunks.split_first().unwrap();
51        command.data = first.to_owned();
52
53        if command.p1 != ChunkPayloadType::First as u8 {
54            return Err(LedgerAppError::InvalidChunkPayloadType);
55        }
56
57        let mut response = self.transport.exchange(&command).await?;
58        match response.error_code() {
59            Ok(APDUErrorCode::NoError) => {}
60            Ok(err) => return Err(LedgerAppError::AppSpecific(err as _, err.description())),
61            Err(err) => return Err(LedgerAppError::Unknown(err as _)),
62        }
63
64        // Send message chunks
65        let p1 = ChunkPayloadType::Subsequent as u8;
66        for chunk in rest {
67            dbg!(&chunk);
68            let command = APDUCommand {
69                cla: command.cla,
70                ins: command.ins,
71                p1,
72                p2: 0,
73                data: chunk.to_vec(),
74            };
75
76            response = self.transport.exchange(&command).await?;
77            match response.error_code() {
78                Ok(APDUErrorCode::NoError) => {}
79                Ok(err) => return Err(LedgerAppError::AppSpecific(err as _, err.description())),
80                Err(err) => return Err(LedgerAppError::Unknown(err as _)),
81            }
82        }
83
84        Ok(response)
85    }
86}