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 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}