1use core::fmt;
2use primitives::{b256, bytes, Address, Bytes, B256};
3
4pub const EIP7702_MAGIC_HASH: B256 =
6 b256!("0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329");
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,
26 pub version: u8,
28 pub raw: Bytes,
30}
31
32impl Eip7702Bytecode {
33 #[inline]
35 pub fn new_raw(raw: Bytes) -> Result<Self, Eip7702DecodeError> {
36 if raw.len() != 23 {
37 return Err(Eip7702DecodeError::InvalidLength);
38 }
39 if !raw.starts_with(&EIP7702_MAGIC_BYTES) {
40 return Err(Eip7702DecodeError::InvalidMagic);
41 }
42
43 if raw[2] != EIP7702_VERSION {
45 return Err(Eip7702DecodeError::UnsupportedVersion);
46 }
47
48 Ok(Self {
49 delegated_address: Address::new(raw[3..].try_into().unwrap()),
50 version: raw[2],
51 raw,
52 })
53 }
54
55 pub fn new(address: Address) -> Self {
57 let mut raw = EIP7702_MAGIC_BYTES.to_vec();
58 raw.push(EIP7702_VERSION);
59 raw.extend(&address);
60 Self {
61 delegated_address: address,
62 version: EIP7702_VERSION,
63 raw: raw.into(),
64 }
65 }
66
67 #[inline]
69 pub fn raw(&self) -> &Bytes {
70 &self.raw
71 }
72
73 #[inline]
75 pub fn address(&self) -> Address {
76 self.delegated_address
77 }
78
79 #[inline]
81 pub fn version(&self) -> u8 {
82 self.version
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89pub enum Eip7702DecodeError {
90 InvalidLength,
94 InvalidMagic,
98 UnsupportedVersion,
102}
103
104impl fmt::Display for Eip7702DecodeError {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 let s = match self {
107 Self::InvalidLength => "Eip7702 is not 23 bytes long",
108 Self::InvalidMagic => "Bytecode is not starting with 0xEF01",
109 Self::UnsupportedVersion => "Unsupported Eip7702 version.",
110 };
111 f.write_str(s)
112 }
113}
114
115impl core::error::Error for Eip7702DecodeError {}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn sanity_decode() {
123 let raw = bytes!("ef01deadbeef");
124 assert_eq!(
125 Eip7702Bytecode::new_raw(raw),
126 Err(Eip7702DecodeError::InvalidLength)
127 );
128
129 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
130 assert_eq!(
131 Eip7702Bytecode::new_raw(raw),
132 Err(Eip7702DecodeError::UnsupportedVersion)
133 );
134
135 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
136 let address = raw[3..].try_into().unwrap();
137 assert_eq!(
138 Eip7702Bytecode::new_raw(raw.clone()),
139 Ok(Eip7702Bytecode {
140 delegated_address: address,
141 version: 0,
142 raw,
143 })
144 );
145 }
146
147 #[test]
148 fn create_eip7702_bytecode_from_address() {
149 let address = Address::new([0x01; 20]);
150 let bytecode = Eip7702Bytecode::new(address);
151 assert_eq!(bytecode.delegated_address, address);
152 assert_eq!(
153 bytecode.raw,
154 bytes!("ef01000101010101010101010101010101010101010101")
155 );
156 }
157}