use {
crate::{
account::AccountView, error::ProgramError, hint::unlikely, impl_sysvar_get,
sysvars::Sysvar, Address,
},
core::mem::{align_of, size_of},
};
pub const RENT_ID: Address = Address::new_from_array([
6, 167, 213, 23, 25, 44, 92, 81, 33, 140, 201, 76, 61, 74, 241, 127, 88, 218, 238, 8, 155, 161,
253, 68, 227, 219, 217, 138, 0, 0, 0, 0,
]);
const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
pub const DEFAULT_LAMPORTS_PER_BYTE: u64 = 6960;
pub const ACCOUNT_STORAGE_OVERHEAD: u64 = 128;
const MAX_LAMPORTS_PER_BYTE: u64 = 1_759_197_129_867;
#[repr(C)]
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone, Debug)]
pub struct Rent {
lamports_per_byte: u64,
}
const _ASSERT_STRUCT_LEN: () = assert!(size_of::<Rent>() == 8);
const _ASSERT_ACCOUNT_ALIGN: () = assert!(align_of::<Rent>() == 8);
impl Rent {
#[inline]
pub fn from_account_view(account_view: &AccountView) -> Result<Rent, ProgramError> {
if unlikely(account_view.is_borrowed_mut()) {
return Err(ProgramError::AccountBorrowFailed);
}
let rent = unsafe { Self::from_account_view_unchecked(account_view) }?;
Ok(rent)
}
#[inline]
pub unsafe fn from_account_view_unchecked(
account_view: &AccountView,
) -> Result<Self, ProgramError> {
if unlikely(account_view.address() != &RENT_ID) {
return Err(ProgramError::InvalidArgument);
}
Ok(Self::from_bytes_unchecked(account_view.borrow_unchecked()))
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProgramError> {
if bytes.len() < size_of::<Self>() {
return Err(ProgramError::InvalidArgument);
}
Ok(unsafe { Self::from_bytes_unchecked(bytes) })
}
#[inline]
pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
Self {
lamports_per_byte: unsafe {
core::ptr::read_unaligned::<u64>(bytes.as_ptr() as *const u64)
},
}
}
#[deprecated(since = "0.10.0", note = "Use `Rent::try_minimum_balance` instead")]
#[inline(always)]
pub fn minimum_balance(&self, data_len: usize) -> u64 {
self.try_minimum_balance(data_len)
.expect("Maximum permitted data length exceeded")
}
#[inline(always)]
pub fn minimum_balance_unchecked(&self, data_len: usize) -> u64 {
(ACCOUNT_STORAGE_OVERHEAD + data_len as u64) * self.lamports_per_byte
}
#[inline(always)]
pub fn try_minimum_balance(&self, data_len: usize) -> Result<u64, ProgramError> {
if data_len as u64 > MAX_PERMITTED_DATA_LENGTH {
return Err(ProgramError::InvalidArgument);
}
if unlikely(self.lamports_per_byte > MAX_LAMPORTS_PER_BYTE) {
return Err(ProgramError::InvalidArgument);
}
Ok(self.minimum_balance_unchecked(data_len))
}
#[allow(deprecated)]
#[inline]
pub fn is_exempt(&self, lamports: u64, data_len: usize) -> bool {
lamports >= self.minimum_balance(data_len)
}
}
impl Sysvar for Rent {
impl_sysvar_get!(RENT_ID, 0);
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use crate::sysvars::rent::{ACCOUNT_STORAGE_OVERHEAD, DEFAULT_LAMPORTS_PER_BYTE};
#[test]
pub fn test_minimum_balance() {
let mut rent = super::Rent {
lamports_per_byte: DEFAULT_LAMPORTS_PER_BYTE,
};
let balance = rent.minimum_balance(100);
let calculated = (ACCOUNT_STORAGE_OVERHEAD + 100) * rent.lamports_per_byte;
assert!(calculated > 0);
assert_eq!(balance, calculated);
rent.lamports_per_byte = DEFAULT_LAMPORTS_PER_BYTE * 2;
let balance = rent.minimum_balance(100);
let calculated = (ACCOUNT_STORAGE_OVERHEAD + 100) * rent.lamports_per_byte;
assert!(calculated > 0);
assert_eq!(balance, calculated);
}
#[test]
pub fn test_from_bytes() {
let rent = super::Rent {
lamports_per_byte: DEFAULT_LAMPORTS_PER_BYTE,
};
let bytes = rent.lamports_per_byte.to_le_bytes();
let deserialized = super::Rent::from_bytes(&bytes).unwrap();
assert_eq!(rent.lamports_per_byte, deserialized.lamports_per_byte);
let bytes = [0u8; 7];
let result = super::Rent::from_bytes(&bytes);
assert!(result.is_err());
}
}