use alloc::string::{String, ToString};
use core::fmt;
use super::v0;
use crate::Felt;
use crate::account::account_id::AccountIdPrefixV0;
use crate::account::{AccountIdV0, AccountIdVersion, AccountStorageMode, AccountType};
use crate::errors::AccountIdError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AccountIdPrefix {
V0(AccountIdPrefixV0),
}
impl AccountIdPrefix {
pub const SERIALIZED_SIZE: usize = 8;
pub fn new_unchecked(prefix: Felt) -> Self {
match v0::extract_version(prefix.as_int())
.expect("prefix should contain a valid account ID version")
{
AccountIdVersion::Version0 => Self::V0(AccountIdPrefixV0::new_unchecked(prefix)),
}
}
pub fn new(prefix: Felt) -> Result<Self, AccountIdError> {
match v0::extract_version(prefix.as_int())? {
AccountIdVersion::Version0 => AccountIdPrefixV0::new(prefix).map(Self::V0),
}
}
pub const fn as_felt(&self) -> Felt {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.as_felt(),
}
}
pub const fn as_u64(&self) -> u64 {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.as_u64(),
}
}
pub const fn account_type(&self) -> AccountType {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.account_type(),
}
}
pub fn is_faucet(&self) -> bool {
self.account_type().is_faucet()
}
pub fn is_regular_account(&self) -> bool {
self.account_type().is_regular_account()
}
pub fn storage_mode(&self) -> AccountStorageMode {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.storage_mode(),
}
}
pub fn has_public_state(&self) -> bool {
self.storage_mode().has_public_state()
}
pub fn is_public(&self) -> bool {
self.storage_mode().is_public()
}
pub fn is_network(&self) -> bool {
self.storage_mode().is_network()
}
pub fn is_private(&self) -> bool {
self.storage_mode().is_private()
}
pub fn version(&self) -> AccountIdVersion {
match self {
AccountIdPrefix::V0(_) => AccountIdVersion::Version0,
}
}
pub fn to_hex(self) -> String {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.to_hex(),
}
}
pub(crate) fn clear_fungible_bit(version: AccountIdVersion, felt: Felt) -> Felt {
match version {
AccountIdVersion::Version0 => {
let clear_fungible_bit_mask = !AccountIdV0::IS_FAUCET_MASK;
Felt::try_from(felt.as_int() & clear_fungible_bit_mask)
.expect("felt should still be valid as we cleared a bit and did not set any")
},
}
}
}
impl From<AccountIdPrefixV0> for AccountIdPrefix {
fn from(id: AccountIdPrefixV0) -> Self {
Self::V0(id)
}
}
impl From<AccountIdPrefix> for Felt {
fn from(id: AccountIdPrefix) -> Self {
match id {
AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
}
}
}
impl From<AccountIdPrefix> for [u8; 8] {
fn from(id: AccountIdPrefix) -> Self {
match id {
AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
}
}
}
impl From<AccountIdPrefix> for u64 {
fn from(id: AccountIdPrefix) -> Self {
match id {
AccountIdPrefix::V0(id_prefix) => id_prefix.into(),
}
}
}
impl TryFrom<[u8; 8]> for AccountIdPrefix {
type Error = AccountIdError;
fn try_from(value: [u8; 8]) -> Result<Self, Self::Error> {
let metadata_byte = value[7];
let version = v0::extract_version(metadata_byte as u64)?;
match version {
AccountIdVersion::Version0 => AccountIdPrefixV0::try_from(value).map(Self::V0),
}
}
}
impl TryFrom<u64> for AccountIdPrefix {
type Error = AccountIdError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
let element = Felt::try_from(value.to_le_bytes().as_slice())
.map_err(AccountIdError::AccountIdInvalidPrefixFieldElement)?;
Self::new(element)
}
}
impl TryFrom<Felt> for AccountIdPrefix {
type Error = AccountIdError;
fn try_from(element: Felt) -> Result<Self, Self::Error> {
Self::new(element)
}
}
impl PartialOrd for AccountIdPrefix {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AccountIdPrefix {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
u64::from(*self).cmp(&u64::from(*other))
}
}
impl fmt::Display for AccountIdPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl Serializable for AccountIdPrefix {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.write_into(target),
}
}
fn get_size_hint(&self) -> usize {
match self {
AccountIdPrefix::V0(id_prefix) => id_prefix.get_size_hint(),
}
}
}
impl Deserializable for AccountIdPrefix {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
<[u8; 8]>::read_from(source)?
.try_into()
.map_err(|err: AccountIdError| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::account::AccountIdV0;
#[test]
fn account_id_prefix_construction() {
for input in [[0xff; 15], [0; 15]] {
for account_type in [
AccountType::FungibleFaucet,
AccountType::NonFungibleFaucet,
AccountType::RegularAccountImmutableCode,
AccountType::RegularAccountUpdatableCode,
] {
for storage_mode in [AccountStorageMode::Private, AccountStorageMode::Public] {
let id = AccountIdV0::dummy(input, account_type, storage_mode);
let prefix = id.prefix();
assert_eq!(prefix.account_type(), account_type);
assert_eq!(prefix.storage_mode(), storage_mode);
assert_eq!(prefix.version(), AccountIdVersion::Version0);
let serialized_prefix = prefix.to_bytes();
AccountIdPrefix::read_from_bytes(&serialized_prefix).unwrap();
assert_eq!(serialized_prefix.len(), AccountIdPrefix::SERIALIZED_SIZE);
}
}
}
}
}