revm_bytecode/
eip7702.rs

1use core::fmt;
2use primitives::{b256, bytes, Address, Bytes, B256};
3
4/// Hash of EF01 bytes that is used for EXTCODEHASH when called from legacy bytecode.
5pub const EIP7702_MAGIC_HASH: B256 =
6    b256!("0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329");
7
8/// EIP-7702 Version Magic in u16 form
9pub const EIP7702_MAGIC: u16 = 0xEF01;
10
11/// EIP-7702 magic number in array form
12pub static EIP7702_MAGIC_BYTES: Bytes = bytes!("ef01");
13
14/// EIP-7702 first version of bytecode
15pub const EIP7702_VERSION: u8 = 0;
16
17/// Bytecode of delegated account, specified in EIP-7702
18///
19/// Format of EIP-7702 bytecode consist of:
20/// `0xEF01` (MAGIC) + `0x00` (VERSION) + 20 bytes of address.
21#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Eip7702Bytecode {
24    /// Address of the delegated account.
25    pub delegated_address: Address,
26    /// Version of the EIP-7702 bytecode. Currently only version 0 is supported.
27    pub version: u8,
28    /// Raw bytecode.
29    pub raw: Bytes,
30}
31
32impl Eip7702Bytecode {
33    /// Creates a new EIP-7702 bytecode or returns None if the raw bytecode is invalid.
34    #[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        // Only supported version is version 0.
44        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    /// Creates a new EIP-7702 bytecode with the given address.
56    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    /// Returns the raw bytecode with version MAGIC number.
68    #[inline]
69    pub fn raw(&self) -> &Bytes {
70        &self.raw
71    }
72
73    /// Returns the address of the delegated contract.
74    #[inline]
75    pub fn address(&self) -> Address {
76        self.delegated_address
77    }
78
79    /// Returns the EIP7702 version of the delegated contract.
80    #[inline]
81    pub fn version(&self) -> u8 {
82        self.version
83    }
84}
85
86/// Bytecode errors
87#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89pub enum Eip7702DecodeError {
90    /// Invalid length of the raw bytecode
91    ///
92    /// It should be 23 bytes.
93    InvalidLength,
94    /// Invalid magic number
95    ///
96    /// All Eip7702 bytecodes should start with the magic number 0xEF01.
97    InvalidMagic,
98    /// Unsupported version
99    ///
100    /// Only supported version is version 0x00
101    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}