use core::fmt;
use primitives::{bytes, Address, Bytes};
pub const EIP7702_MAGIC: u16 = 0xEF01;
pub static EIP7702_MAGIC_BYTES: Bytes = bytes!("ef01");
pub const EIP7702_VERSION: u8 = 0;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Eip7702Bytecode {
pub delegated_address: Address,
pub version: u8,
pub raw: Bytes,
}
impl Eip7702Bytecode {
#[inline]
pub fn new_raw(raw: Bytes) -> Result<Self, Eip7702DecodeError> {
if raw.len() != 23 {
return Err(Eip7702DecodeError::InvalidLength);
}
if !raw.starts_with(&EIP7702_MAGIC_BYTES) {
return Err(Eip7702DecodeError::InvalidMagic);
}
if raw[2] != EIP7702_VERSION {
return Err(Eip7702DecodeError::UnsupportedVersion);
}
Ok(Self {
delegated_address: Address::new(raw[3..].try_into().unwrap()),
version: EIP7702_VERSION,
raw,
})
}
pub fn new(address: Address) -> Self {
let mut raw = EIP7702_MAGIC_BYTES.to_vec();
raw.push(EIP7702_VERSION);
raw.extend(&address);
Self {
delegated_address: address,
version: EIP7702_VERSION,
raw: raw.into(),
}
}
#[inline]
pub fn raw(&self) -> &Bytes {
&self.raw
}
#[inline]
pub fn address(&self) -> Address {
self.delegated_address
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Eip7702DecodeError {
InvalidLength,
InvalidMagic,
UnsupportedVersion,
}
impl fmt::Display for Eip7702DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::InvalidLength => "Eip7702 is not 23 bytes long",
Self::InvalidMagic => "Bytecode is not starting with 0xEF01",
Self::UnsupportedVersion => "Unsupported Eip7702 version.",
};
f.write_str(s)
}
}
impl core::error::Error for Eip7702DecodeError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanity_decode() {
let raw = bytes!("ef01deadbeef");
assert_eq!(
Eip7702Bytecode::new_raw(raw),
Err(Eip7702DecodeError::InvalidLength)
);
let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
assert_eq!(
Eip7702Bytecode::new_raw(raw),
Err(Eip7702DecodeError::UnsupportedVersion)
);
let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
let address = raw[3..].try_into().unwrap();
assert_eq!(
Eip7702Bytecode::new_raw(raw.clone()),
Ok(Eip7702Bytecode {
delegated_address: address,
version: 0,
raw,
})
);
}
#[test]
fn create_eip7702_bytecode_from_address() {
let address = Address::new([0x01; 20]);
let bytecode = Eip7702Bytecode::new(address);
assert_eq!(bytecode.delegated_address, address);
assert_eq!(
bytecode.raw,
bytes!("ef01000101010101010101010101010101010101010101")
);
}
}