diff --git a/programs/vote_api/src/vote_instruction.rs b/programs/vote_api/src/vote_instruction.rs index 8786b63103..f122bc9aa7 100644 --- a/programs/vote_api/src/vote_instruction.rs +++ b/programs/vote_api/src/vote_instruction.rs @@ -35,6 +35,12 @@ fn initialize_account(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8 ) } +pub fn minimum_balance() -> u64 { + let rent = solana_sdk::rent::Rent::default(); + + rent.minimum_balance(VoteState::size_of()) +} + pub fn create_account( from_pubkey: &Pubkey, vote_pubkey: &Pubkey, @@ -222,4 +228,12 @@ mod tests { ); } + #[test] + fn test_minimum_balance() { + let rent = solana_sdk::rent::Rent::default(); + let minimum_balance = rent.minimum_balance(VoteState::size_of()); + // vote state cheaper than "my $0.02" ;) + assert!(minimum_balance as f64 / 2f64.powf(34.0) < 0.02) + } + } diff --git a/sdk/src/genesis_block.rs b/sdk/src/genesis_block.rs index 305d09c47d..4bb6fba838 100644 --- a/sdk/src/genesis_block.rs +++ b/sdk/src/genesis_block.rs @@ -6,6 +6,7 @@ use crate::hash::{hash, Hash}; use crate::inflation::Inflation; use crate::poh_config::PohConfig; use crate::pubkey::Pubkey; +use crate::rent::Rent; use crate::signature::{Keypair, KeypairUtil}; use crate::system_program; use crate::timing::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT}; @@ -28,6 +29,7 @@ pub struct GenesisBlock { pub poh_config: PohConfig, pub fee_calculator: FeeCalculator, pub inflation: Inflation, + pub rent: Rent, } // useful for basic tests @@ -59,6 +61,7 @@ impl Default for GenesisBlock { poh_config: PohConfig::default(), inflation: Inflation::default(), fee_calculator: FeeCalculator::default(), + rent: Rent::default(), } } } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 37ca5a4be8..24287c92b6 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -14,6 +14,7 @@ pub mod native_loader; pub mod packet; pub mod poh_config; pub mod pubkey; +pub mod rent; pub mod rpc_port; pub mod short_vec; pub mod signature; diff --git a/sdk/src/rent.rs b/sdk/src/rent.rs new file mode 100644 index 0000000000..9e3c0acbec --- /dev/null +++ b/sdk/src/rent.rs @@ -0,0 +1,72 @@ +//! configuration for network rent + +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +pub struct Rent { + /// Rental rate + pub lamports_per_byte_year: u64, + + /// exemption threshold, in years + pub exemption_threshold: f64, +} + +/// default rental rate in lamports/byte-year, based on: +/// 2^^34 lamports per Sol +/// $1 per Sol +/// $0.01 per megabyte day +/// $3.65 per megabyte year +pub const DEFAULT_LAMPORTS_PER_BYTE_YEAR: u64 = 17_179_869_184 / 100 * 365 / (1024 * 1024); + +/// default amount of time (in years) the balance has to include rent for +pub const DEFAULT_EXEMPTION_THRESHOLD: f64 = 2.0; + +impl Default for Rent { + fn default() -> Self { + Self { + lamports_per_byte_year: DEFAULT_LAMPORTS_PER_BYTE_YEAR, + exemption_threshold: DEFAULT_EXEMPTION_THRESHOLD, + } + } +} + +impl Rent { + /// minimum balance due for a given size Account::data.len() + pub fn minimum_balance(&self, data_len: usize) -> u64 { + let bytes = data_len as u64; + bytes * (self.exemption_threshold * self.lamports_per_byte_year as f64) as u64 + } + + /// whether a given balance and data_len would be exempt + pub fn is_exempt(&self, balance: u64, data_len: usize) -> bool { + balance >= self.minimum_balance(data_len) + } + + /// rent due on account's data_len with balance + pub fn due(&self, balance: u64, data_len: usize, years_elapsed: f64) -> u64 { + if self.is_exempt(balance, data_len) { + 0 + } else { + let bytes = data_len as u64; + ((self.lamports_per_byte_year * bytes) as f64 * years_elapsed) as u64 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_due() { + let rent = Rent::default(); + + assert_eq!(rent.due(0, 1, 1.0), DEFAULT_LAMPORTS_PER_BYTE_YEAR); + assert_eq!( + rent.due( + DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64, + 1, + 1.0 + ), + 0 + ); + } +}