use candid::CandidType;
use serde::{Deserialize, Serialize};
pub type CanisterId = candid::Principal; pub type CanisterIdText = String;
pub type CollectionId = candid::Principal; pub type CollectionIdText = String;
pub type UserId = candid::Principal; pub type UserIdText = String;
pub type CallerId = candid::Principal; pub type CallerIdText = String;
#[derive(CandidType, Serialize, Deserialize, Debug, Clone, Copy, Default)]
pub struct Subaccount([u8; 32]); pub type SubaccountHex = String;
#[derive(CandidType, Serialize, Deserialize, Debug, Clone, Copy)]
pub struct AccountIdentifier([u8; 32]); pub type AccountIdentifierHex = String;
#[derive(Debug)]
pub enum FromVecError {
InvalidLength, }
impl std::fmt::Display for FromVecError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidLength => write!(f, "The bytes length of account must be 32"),
}
}
}
impl std::error::Error for FromVecError {}
#[derive(Debug)]
pub enum FromHexError {
HexError(hex::FromHexError),
InvalidLength, }
impl std::fmt::Display for FromHexError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::HexError(e) => write!(f, "HexError: {e}"), Self::InvalidLength => write!(f, "The bytes length of account must be 32"),
}
}
}
impl std::error::Error for FromHexError {}
impl From<FromVecError> for FromHexError {
fn from(value: FromVecError) -> Self {
match value {
FromVecError::InvalidLength => FromHexError::InvalidLength,
}
}
}
impl From<[u8; 32]> for Subaccount {
fn from(value: [u8; 32]) -> Self {
Subaccount(value)
}
}
impl From<u64> for Subaccount {
fn from(value: u64) -> Self {
let mut subaccount: [u8; 32] = [0; 32];
subaccount[24..].copy_from_slice(&value.to_be_bytes());
subaccount.into()
}
}
impl Subaccount {
#[allow(unused)]
fn into_inner(self) -> [u8; 32] {
self.0
}
}
impl From<[u8; 32]> for AccountIdentifier {
fn from(value: [u8; 32]) -> Self {
AccountIdentifier(value)
}
}
impl TryFrom<&[u8]> for AccountIdentifier {
type Error = FromVecError;
#[inline]
fn try_from(account: &[u8]) -> Result<Self, Self::Error> {
Ok(AccountIdentifier(parse_account(account)?))
}
}
impl TryFrom<&str> for AccountIdentifier {
type Error = FromHexError;
#[inline]
fn try_from(account_identifier_hex: &str) -> Result<Self, Self::Error> {
let account = hex::decode(account_identifier_hex).map_err(FromHexError::HexError)?;
Ok((&account[..]).try_into()?)
}
}
impl AccountIdentifier {
#[allow(unused)]
#[inline]
pub fn into_inner(self) -> [u8; 32] {
self.0
}
#[inline]
pub fn from(owner: &UserId, subaccount: &Option<Subaccount>) -> Self {
let subaccount: Subaccount = (*subaccount).unwrap_or_default();
use sha2::Digest;
let mut hasher = sha2::Sha224::new(); hasher.update(b"\x0Aaccount-id");
hasher.update(owner.as_slice());
hasher.update(&subaccount.0[..]);
let hash: [u8; 28] = hasher.finalize().into();
let mut hasher = crc32fast::Hasher::new();
hasher.update(&hash);
let crc32_bytes = hasher.finalize().to_be_bytes();
let mut result: [u8; 32] = [0u8; 32];
result[0..4].copy_from_slice(&crc32_bytes[..]); result[4..32].copy_from_slice(hash.as_ref());
result.into()
}
#[inline]
pub fn from_vec(owner: &UserId, subaccount: &Option<Vec<u8>>) -> Result<Self, FromVecError> {
let subaccount = subaccount
.as_ref()
.map(|a| parse_account(a.as_ref()))
.transpose()?
.map(|subaccount| subaccount.into());
Ok(Self::from(owner, &subaccount))
}
#[inline]
pub fn to_hex(&self) -> String {
hex::encode(self.0)
}
}
#[inline]
fn parse_account(account: &[u8]) -> Result<[u8; 32], FromVecError> {
if account.len() != 32 {
return Err(FromVecError::InvalidLength);
}
let mut array: [u8; 32] = [0; 32];
array.copy_from_slice(account);
Ok(array)
}
#[inline]
pub fn caller() -> CallerId {
ic_cdk::api::caller()
}
#[inline]
pub fn self_canister_id() -> CanisterId {
ic_cdk::api::id()
}