ssi_eip712/
lib.rs

1use keccak_hash::keccak;
2use serde::{Deserialize, Serialize};
3
4mod encode;
5mod hashing;
6mod ty;
7mod value;
8
9pub use hashing::TypedDataHashError;
10pub use ty::*;
11pub use value::*;
12
13#[derive(Debug, Serialize, Deserialize, Clone)]
14#[serde(rename_all = "camelCase")]
15pub struct TypedData {
16    pub types: Types,
17    pub primary_type: StructName,
18    pub domain: Value,
19    pub message: Value,
20}
21
22impl TypedData {
23    /// Encode a typed data message for hashing and signing.
24    /// [Reference](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#specification)
25    pub fn hash(&self) -> Result<[u8; 32], TypedDataHashError> {
26        let bytes = self.encode()?;
27        Ok(keccak(bytes).to_fixed_bytes())
28    }
29
30    pub fn encode(&self) -> Result<[u8; 66], TypedDataHashError> {
31        let message_hash = self.message.hash(&self.primary_type, &self.types)?;
32        let domain_separator = self
33            .domain
34            .hash(&StructName::from("EIP712Domain"), &self.types)?;
35
36        let mut result = [0; 66];
37        result[0] = 0x19;
38        result[1] = 0x01;
39        result[2..34].copy_from_slice(&domain_separator);
40        result[34..].copy_from_slice(&message_hash);
41
42        Ok(result)
43    }
44}
45
46pub(crate) fn bytes_from_hex(s: &str) -> Option<Vec<u8>> {
47    s.strip_prefix("0x")
48        .and_then(|hex_str| hex::decode(hex_str).ok())
49}