use crate::cell::*;
use crate::dict::*;
use crate::error::*;
use crate::num::*;
use crate::models::currency::CurrencyCollection;
use crate::models::message::IntAddr;
use crate::models::Lazy;
#[derive(Debug, Default, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StorageUsed {
pub cells: VarUint56,
pub bits: VarUint56,
pub public_cells: VarUint56,
}
impl StorageUsed {
pub const ZERO: Self = Self {
cells: VarUint56::ZERO,
bits: VarUint56::ZERO,
public_cells: VarUint56::ZERO,
};
pub fn compute(account: &Account, cell_limit: usize) -> Result<Self, Error> {
let cell = {
let cx = &mut Cell::empty_context();
let mut storage = CellBuilder::new();
storage.store_u64(account.last_trans_lt)?;
account.balance.store_into(&mut storage, cx)?;
account.state.store_into(&mut storage, cx)?;
if account.init_code_hash.is_some() {
account.init_code_hash.store_into(&mut storage, cx)?;
}
storage.build_ext(cx)?
};
let Some(res) = cell.compute_unique_stats(cell_limit) else {
return Err(Error::Cancelled);
};
let res = Self {
cells: VarUint56::new(res.cell_count),
bits: VarUint56::new(res.bit_count),
public_cells: Default::default(),
};
if res.cells.is_valid() || !res.bits.is_valid() {
return Err(Error::IntOverflow);
}
Ok(res)
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StorageUsedShort {
pub cells: VarUint56,
pub bits: VarUint56,
}
impl StorageUsedShort {
pub const ZERO: Self = Self {
cells: VarUint56::ZERO,
bits: VarUint56::ZERO,
};
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StorageInfo {
pub used: StorageUsed,
pub last_paid: u32,
pub due_payment: Option<Tokens>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AccountStatus {
Uninit = 0b00,
Frozen = 0b01,
Active = 0b10,
NotExists = 0b11,
}
impl AccountStatus {
pub const BITS: u16 = 2;
}
impl Store for AccountStatus {
#[inline]
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
builder.store_small_uint(*self as u8, 2)
}
}
impl<'a> Load<'a> for AccountStatus {
#[inline]
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(2) {
Ok(ty) => Ok(match ty {
0b00 => Self::Uninit,
0b01 => Self::Frozen,
0b10 => Self::Active,
0b11 => Self::NotExists,
_ => {
debug_assert!(false, "unexpected small uint");
unsafe { std::hint::unreachable_unchecked() }
}
}),
Err(e) => Err(e),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ShardAccount {
pub account: Lazy<OptionalAccount>,
pub last_trans_hash: HashBytes,
pub last_trans_lt: u64,
}
impl ShardAccount {
pub fn load_account(&self) -> Result<Option<Account>, Error> {
let OptionalAccount(account) = ok!(self.account.load());
Ok(account)
}
}
#[derive(Default, Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OptionalAccount(pub Option<Account>);
impl OptionalAccount {
pub const EMPTY: Self = Self(None);
pub fn status(&self) -> AccountStatus {
match &self.0 {
None => AccountStatus::NotExists,
Some(account) => account.state.status(),
}
}
pub fn last_trans_lt(&self) -> u64 {
match &self.0 {
None => 0,
Some(account) => account.last_trans_lt,
}
}
#[cfg(feature = "sync")]
pub fn balance(&self) -> &CurrencyCollection {
static DEFAULT_VALANCE: CurrencyCollection = CurrencyCollection::ZERO;
match &self.0 {
None => &DEFAULT_VALANCE,
Some(account) => &account.balance,
}
}
pub fn state(&self) -> Option<&AccountState> {
Some(&self.0.as_ref()?.state)
}
}
impl AsRef<Option<Account>> for OptionalAccount {
#[inline]
fn as_ref(&self) -> &Option<Account> {
&self.0
}
}
impl AsMut<Option<Account>> for OptionalAccount {
#[inline]
fn as_mut(&mut self) -> &mut Option<Account> {
&mut self.0
}
}
impl Store for OptionalAccount {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
match &self.0 {
None => builder.store_bit_zero(),
Some(account) => {
let with_init_code_hash = account.init_code_hash.is_some();
ok!(if with_init_code_hash {
builder.store_small_uint(0b0001, 4)
} else {
builder.store_bit_one()
});
ok!(account.address.store_into(builder, context));
ok!(account.storage_stat.store_into(builder, context));
ok!(builder.store_u64(account.last_trans_lt));
ok!(account.balance.store_into(builder, context));
ok!(account.state.store_into(builder, context));
if let Some(init_code_hash) = &account.init_code_hash {
ok!(builder.store_bit_one());
builder.store_u256(init_code_hash)
} else {
Ok(())
}
}
}
}
}
impl<'a> Load<'a> for OptionalAccount {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let with_init_code_hash = if ok!(slice.load_bit()) {
false } else if slice.is_data_empty() {
return Ok(Self::EMPTY);
} else {
let tag = ok!(slice.load_small_uint(3));
match tag {
0 => false, 1 => true, _ => return Err(Error::InvalidData),
}
};
Ok(Self(Some(Account {
address: ok!(IntAddr::load_from(slice)),
storage_stat: ok!(StorageInfo::load_from(slice)),
last_trans_lt: ok!(slice.load_u64()),
balance: ok!(CurrencyCollection::load_from(slice)),
state: ok!(AccountState::load_from(slice)),
init_code_hash: if with_init_code_hash {
ok!(Option::<HashBytes>::load_from(slice))
} else {
None
},
})))
}
}
impl From<Account> for OptionalAccount {
#[inline]
fn from(value: Account) -> Self {
Self(Some(value))
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Account {
pub address: IntAddr,
pub storage_stat: StorageInfo,
pub last_trans_lt: u64,
pub balance: CurrencyCollection,
pub state: AccountState,
pub init_code_hash: Option<HashBytes>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "status"))]
pub enum AccountState {
Uninit,
Active(StateInit),
Frozen(HashBytes),
}
impl AccountState {
pub fn status(&self) -> AccountStatus {
match self {
Self::Uninit => AccountStatus::Uninit,
Self::Active(_) => AccountStatus::Active,
Self::Frozen(_) => AccountStatus::Frozen,
}
}
}
impl Store for AccountState {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
match self {
Self::Uninit => builder.store_small_uint(0b00, 2),
Self::Active(state) => {
ok!(builder.store_bit_one());
state.store_into(builder, context)
}
Self::Frozen(hash) => {
ok!(builder.store_small_uint(0b01, 2));
builder.store_u256(hash)
}
}
}
}
impl<'a> Load<'a> for AccountState {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(if ok!(slice.load_bit()) {
match StateInit::load_from(slice) {
Ok(state) => Self::Active(state),
Err(e) => return Err(e),
}
} else if ok!(slice.load_bit()) {
match slice.load_u256() {
Ok(state) => Self::Frozen(state),
Err(e) => return Err(e),
}
} else {
Self::Uninit
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StateInit {
pub split_depth: Option<SplitDepth>,
pub special: Option<SpecialFlags>,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub code: Option<Cell>,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub data: Option<Cell>,
pub libraries: Dict<HashBytes, SimpleLib>,
}
impl Default for StateInit {
fn default() -> Self {
Self {
split_depth: None,
special: None,
code: None,
data: None,
libraries: Dict::new(),
}
}
}
impl StateInit {
pub const fn exact_size_const(&self) -> Size {
Size {
bits: self.bit_len(),
refs: self.reference_count(),
}
}
const fn bit_len(&self) -> u16 {
(1 + self.split_depth.is_some() as u16 * SplitDepth::BITS)
+ (1 + self.special.is_some() as u16 * SpecialFlags::BITS)
+ 3
}
const fn reference_count(&self) -> u8 {
self.code.is_some() as u8 + self.data.is_some() as u8 + !self.libraries.is_empty() as u8
}
}
impl ExactSize for StateInit {
#[inline]
fn exact_size(&self) -> Size {
self.exact_size_const()
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SpecialFlags {
pub tick: bool,
pub tock: bool,
}
impl SpecialFlags {
pub const BITS: u16 = 2;
}
impl Store for SpecialFlags {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
builder.store_small_uint(((self.tick as u8) << 1) | self.tock as u8, 2)
}
}
impl<'a> Load<'a> for SpecialFlags {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(2) {
Ok(data) => Ok(Self {
tick: data & 0b10 != 0,
tock: data & 0b01 != 0,
}),
Err(e) => Err(e),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SimpleLib {
pub public: bool,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub root: Cell,
}