1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Rent-exemption helpers.
//!
//! Solana's rent model charges accounts for storage on a per-byte-year
//! basis. An account that holds at least
//! `(data_len + ACCOUNT_STORAGE_OVERHEAD) * LAMPORTS_PER_BYTE_YEAR *
//! EXEMPTION_THRESHOLD` lamports is *rent-exempt* and never loses
//! balance to rent collection.
//!
//! This module exposes two things:
//!
//! 1. [`minimum_balance`] - a pure snapshot calculation using the launch-era
//! constants (`lamports_per_byte_year = 3480`, `exemption_threshold = 2
//! years`, `account_storage_overhead = 128 bytes`). It is useful for host
//! tests and fixed-config calculations, but is not authoritative after an
//! on-chain rent reprice.
//!
//! 2. [`check_rent_exempt`] - the runtime guard backing the
//! `#[account(rent_exempt = enforce)]` field keyword emitted by
//! `#[hopper::context]`. Compares `account.lamports()` to the live Rent
//! sysvar minimum and returns
//! `ProgramError::AccountNotRentExempt` (a builtin variant mapping
//! to Solana's canonical code) on failure.
//!
//! The enforcement path deliberately reads `sol_get_rent_sysvar`. Rent is a
//! runtime-owned parameter, so a safety gate must fail closed if that read
//! fails rather than accepting an account against stale constants.
use crateAccountView;
use crateProgramError;
use crateProgramResult;
/// Lamports charged per byte of account storage per year.
///
/// Launch-era snapshot. SIMD-0194 moved the full effective price into the
/// first Rent-sysvar field, and SIMD-0437 began repricing it in September
/// 2026. Runtime decisions must use [`minimum_balance_live`].
pub const LAMPORTS_PER_BYTE_YEAR: u64 = 3_480;
/// Years of rent an account must prepay to be exempt.
///
/// Launch-era snapshot. SIMD-0194 deprecated the threshold and changed its
/// live wire marker to `1.0`; this constant exists only for the paired legacy
/// calculation below.
pub const EXEMPTION_THRESHOLD_YEARS: u64 = 2;
/// Fixed per-account storage overhead the cluster charges on top of
/// user data. 128 bytes (header + metadata).
pub const ACCOUNT_STORAGE_OVERHEAD: u64 = 128;
/// Minimum lamport balance for an account with `data_len` bytes of data under
/// Solana's launch-era rent snapshot.
///
/// `(data_len + 128) * 3480 * 2` - constant-folded at the call site
/// when `data_len` is a `const`.
pub const
/// Rent-exempt minimum read from the **live** Rent sysvar on-chain.
/// Host tests use the compile-time snapshot because no runtime sysvar exists.
///
/// Use this for value-bearing decisions, funding a new account, the
/// realloc top-up; so that if the cluster ever re-governs the rent
/// parameters, Hopper charges the live amount rather than a stale
/// hard-coded one. An on-chain sysvar read failure is returned to the caller;
/// value-bearing checks must not silently fall back to stale constants.
/// Assert that `account` holds enough lamports to be rent-exempt for
/// its current data length. Used by the `#[account(rent_exempt =
/// enforce)]` constraint lowering in `hopper-derive`.
///
/// Returns `ProgramError::AccountNotRentExempt` on underrun (builtin
/// index 14 in Hopper's error ABI, matching Solana's canonical
/// `AccountNotRentExempt` code).