use core::fmt;
use primitives::{b256, bytes, Address, Bytes, B256};
pub const OWNABLE_ACCOUNT_MAGIC_HASH: B256 =
b256!("0x85160e14613bd11c0e87050b7f84bbea3095f7f0ccd58026f217fdff9043c16b");
pub const OWNABLE_ACCOUNT_MAGIC: u16 = 0xEF44;
pub static OWNABLE_ACCOUNT_MAGIC_BYTES: Bytes = bytes!("ef44");
pub const OWNABLE_ACCOUNT_VERSION: u8 = 0;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OwnableAccountBytecode {
pub owner_address: Address,
pub version: u8,
pub metadata: Bytes,
pub raw: Bytes,
}
impl OwnableAccountBytecode {
#[inline]
pub fn new_raw(raw: Bytes) -> Result<Self, OwnableAccountDecodeError> {
if raw.len() < 23 {
return Err(OwnableAccountDecodeError::InvalidLength);
} else if !raw.starts_with(&OWNABLE_ACCOUNT_MAGIC_BYTES) {
return Err(OwnableAccountDecodeError::InvalidMagic);
}
if raw[2] != OWNABLE_ACCOUNT_VERSION {
return Err(OwnableAccountDecodeError::UnsupportedVersion);
}
Ok(Self {
owner_address: Address::new(raw[3..23].try_into().unwrap()),
version: OWNABLE_ACCOUNT_VERSION,
metadata: raw.slice(23..),
raw,
})
}
pub fn new(address: Address, metadata: Bytes) -> Self {
let mut raw = OWNABLE_ACCOUNT_MAGIC_BYTES.to_vec();
raw.push(OWNABLE_ACCOUNT_VERSION);
raw.extend(&address);
raw.extend(&metadata);
Self {
owner_address: address,
version: OWNABLE_ACCOUNT_VERSION,
metadata,
raw: raw.into(),
}
}
#[inline]
pub fn metadata(&self) -> &Bytes {
&self.metadata
}
#[inline]
pub fn owner(&self) -> Address {
self.owner_address
}
#[inline]
pub fn raw(&self) -> &Bytes {
&self.raw
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OwnableAccountDecodeError {
InvalidLength,
InvalidMagic,
UnsupportedVersion,
}
impl fmt::Display for OwnableAccountDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::InvalidLength => "Metadata is not 23 bytes long",
Self::InvalidMagic => "Metadata is not starting with 0xEF44",
Self::UnsupportedVersion => "Unsupported Metadata version.",
};
f.write_str(s)
}
}
impl core::error::Error for OwnableAccountDecodeError {}
#[cfg(test)]
mod tests {
use super::*;
use primitives::keccak256;
#[test]
fn magic_bytes_hash_check() {
let result = keccak256(&OWNABLE_ACCOUNT_MAGIC_BYTES);
assert_eq!(OWNABLE_ACCOUNT_MAGIC_HASH.as_slice(), result.as_slice());
}
#[test]
fn sanity_decode() {
let metadata = bytes!("ef44deadbeef");
assert_eq!(
OwnableAccountBytecode::new_raw(metadata),
Err(OwnableAccountDecodeError::InvalidLength)
);
let metadata = bytes!("ef4401deadbeef00000000000000000000000000000000");
assert_eq!(
OwnableAccountBytecode::new_raw(metadata),
Err(OwnableAccountDecodeError::UnsupportedVersion)
);
let raw = bytes!("ef4400deadbeef00000000000000000000000000000000");
let address = raw[3..].try_into().unwrap();
assert_eq!(
OwnableAccountBytecode::new_raw(raw.clone()),
Ok(OwnableAccountBytecode {
owner_address: address,
version: 0,
metadata: raw.slice(23..),
raw,
})
);
}
#[test]
fn create_metadata_from_address() {
let address = Address::new([0x01; 20]);
let bytecode = OwnableAccountBytecode::new(address, bytes!("0102030405"));
assert_eq!(bytecode.owner_address, address);
assert_eq!(bytecode.metadata, bytes!("0102030405"));
}
}