1use core::fmt;
2use primitives::{b256, bytes, Address, Bytes, B256};
3
4pub const EIP7702_MAGIC_HASH: B256 =
6 b256!("eadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329");
7
8pub const EIP7702_MAGIC: u16 = 0xEF01;
10
11pub static EIP7702_MAGIC_BYTES: Bytes = bytes!("ef01");
13
14pub const EIP7702_VERSION: u8 = 0;
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Eip7702Bytecode {
24 pub delegated_address: Address,
25 pub version: u8,
26 pub raw: Bytes,
27}
28
29impl Eip7702Bytecode {
30 #[inline]
32 pub fn new_raw(raw: Bytes) -> Result<Self, Eip7702DecodeError> {
33 if raw.len() != 23 {
34 return Err(Eip7702DecodeError::InvalidLength);
35 }
36 if !raw.starts_with(&EIP7702_MAGIC_BYTES) {
37 return Err(Eip7702DecodeError::InvalidMagic);
38 }
39
40 if raw[2] != EIP7702_VERSION {
42 return Err(Eip7702DecodeError::UnsupportedVersion);
43 }
44
45 Ok(Self {
46 delegated_address: Address::new(raw[3..].try_into().unwrap()),
47 version: EIP7702_VERSION,
48 raw,
49 })
50 }
51
52 pub fn new(address: Address) -> Self {
54 let mut raw = EIP7702_MAGIC_BYTES.to_vec();
55 raw.push(EIP7702_VERSION);
56 raw.extend(&address);
57 Self {
58 delegated_address: address,
59 version: EIP7702_VERSION,
60 raw: raw.into(),
61 }
62 }
63
64 #[inline]
66 pub fn raw(&self) -> &Bytes {
67 &self.raw
68 }
69
70 #[inline]
72 pub fn address(&self) -> Address {
73 self.delegated_address
74 }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub enum Eip7702DecodeError {
81 InvalidLength,
85 InvalidMagic,
89 UnsupportedVersion,
93}
94
95impl fmt::Display for Eip7702DecodeError {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 let s = match self {
98 Self::InvalidLength => "Eip7702 is not 23 bytes long",
99 Self::InvalidMagic => "Bytecode is not starting with 0xEF01",
100 Self::UnsupportedVersion => "Unsupported Eip7702 version.",
101 };
102 f.write_str(s)
103 }
104}
105
106impl core::error::Error for Eip7702DecodeError {}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn sanity_decode() {
114 let raw = bytes!("ef01deadbeef");
115 assert_eq!(
116 Eip7702Bytecode::new_raw(raw),
117 Err(Eip7702DecodeError::InvalidLength)
118 );
119
120 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
121 assert_eq!(
122 Eip7702Bytecode::new_raw(raw),
123 Err(Eip7702DecodeError::UnsupportedVersion)
124 );
125
126 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
127 let address = raw[3..].try_into().unwrap();
128 assert_eq!(
129 Eip7702Bytecode::new_raw(raw.clone()),
130 Ok(Eip7702Bytecode {
131 delegated_address: address,
132 version: 0,
133 raw,
134 })
135 );
136 }
137
138 #[test]
139 fn create_eip7702_bytecode_from_address() {
140 let address = Address::new([0x01; 20]);
141 let bytecode = Eip7702Bytecode::new(address);
142 assert_eq!(bytecode.delegated_address, address);
143 assert_eq!(
144 bytecode.raw,
145 bytes!("ef01000101010101010101010101010101010101010101")
146 );
147 }
148}