Solana rent reduction — 2026-09
Scope: an assessment of what Solana’s SIMD-0437 rent reduction (the “Reduced Rent” upgrade, see Sources) means for the SithBit protocol, the on-chain programs, the host-side clients and the docs. It records what the upgrade changes, how each part of this tree reads rent today, the findings under the launch assumption below, and the implementation notes for the follow-ups the backlog names. The findings are inputs to planning, not decisions — nothing on this page changes code. It is the structural sibling of JMAP feasibility — 2026-09.
Launch assumption (user, 2026-09-15): SithBit does not deploy to mainnet until SIMD-0437-5 is active, so every mainnet account is created at the final rate and the surplus-rent migration class never arises on mainnet. The self-alias claim-fee waiver in F6 is assumed adopted; the cost tables show both cases.
Method: the upgrade facts come from the Solana upgrade page and the
Solana Foundation’s data analysis; the cluster state is read live with the
Solana CLI on 2026-09-15 (solana rent 0 -u <cluster> and solana feature status <gate> -u <cluster>), not from the article’s dates. Every claim
about this tree names the symbol it reads.
Verdict
The programs read the live Rent sysvar, so new accounts get the cheaper rate with no protocol or ABI change. The host-side quotes, the program test genesis and the docs’ fixed figures still assume the SDK default schedule and need updating before launch. The one behaviour to harden is the set of paths that derive value as “balance minus live rent”, which is cheap while the ABI is unfrozen and a migration afterwards.
What SIMD-0437 changes
The rent-exempt minimum stays (128 + data_len) × lamports_per_byte; only
the constant moves, in feature-gated steps. Existing accounts keep the
balance they hold; nothing is refunded automatically. Excess is recovered
only by closing the account or through an instruction the owning program
provides (SPL Token’s WithdrawExcessLamports is the model). A reverse
gate exists in case state growth misbehaves, and SIMD-0392 covers an
increase.
| Step | Gate (prefix) | lamports/byte | devnet | mainnet-beta |
|---|---|---|---|---|
| SIMD-0437-1 | 4a6f7o7i… | 6,333 | active | active |
| SIMD-0437-2 | 61BtM7Bk… | 5,080 | active | active |
| SIMD-0437-3 | Ftxb3ZKq… | 2,575 | not yet allowed | not yet allowed |
| SIMD-0437-4 | GsUBNYND… | 1,322 | not yet allowed | not yet allowed |
| SIMD-0437-5 | mZdnRh9T… | 696 | not yet allowed | not yet allowed |
Cluster state as read on 2026-09-15: devnet, testnet and mainnet-beta all
answer solana rent 0 with 650,240 (= 128 × 5,080). The remaining steps
are slated for Agave 4.4, expected around November 2026. The SDK constant
behind Rent::default() (the solana-rent crate in Cargo.lock) is
still 6,960 and does not track the cluster.
Rent per SithBit account class
Rent-exempt minimum in lamports by schedule. Data sizes are the fixed-size
constants in mail_model::constants where one exists, and the default
instance’s borsh length otherwise (a real mailbox or frombox with longer
strings is larger).
| Account | data bytes | at 6,960 | at 5,080 | at 696 |
|---|---|---|---|---|
message (Email, 46-byte CID) | 178 | 2,129,760 | 1,554,480 | 212,976 |
frombox (Frombox, default instance) | 20 | 1,030,080 | 751,840 | 103,008 |
sender reputation (SenderReputation) | 40 | 1,169,280 | 853,440 | 116,928 |
mailbox (Mailbox, default instance) | 77 | 1,426,800 | 1,041,400 | 142,680 |
alias (Alias) | 32 | 1,113,600 | 812,800 | 111,360 |
participant beacon (PARTICIPANT_BEACON_LEN) | 145 | 1,900,080 | 1,386,840 | 190,008 |
pin lease (PIN_LEASE_LEN) | 72 | 1,392,000 | 1,016,000 | 139,200 |
alias listing (ALIAS_LISTING_LEN) | 105 | 1,621,680 | 1,183,640 | 162,168 |
encryption key (MAX_ENCRYPTION_KEY_LENGTH key) | 516 | 4,482,240 | 3,271,520 | 448,224 |
proof witness (MAX_PROOF_WITNESS_LEN + header) | 8,264 | 58,408,320 | 42,631,360 | 5,840,832 |
The postage economics are untouched: the spam deterrent is the stamp price, and rent is a refundable deposit in every account class. The visible upside is bulk sending, where a campaign’s upfront capital is dominated by per-message rent and drops about 10× with no code change.
How the code reads rent today
- On-chain:
get_minimum_account_balanceinprogram_commonreadsRent::get(), the live sysvar. EveryCreateAccountand every resize top-up in the programs sizes from it, so they are correct at any step. - Host-side:
get_minimum_account_balanceinsolana_commonreadsRent::default(), the SDK constant. Its callers arerequired_mailbox_funding_for_alias(the CLI setup wizard and, throughrequired_mailbox_funding_for_handleinmail_wasm, the web onboarding figure) and the rent legs ofmail_client’s campaign pricing. The pin-lease command and the devnet integration tests fetch the live value over RPC. - Program tests:
solana-program-testgenesis carries the SDK default, and the suites’rent_forhelper computes fixture balances the same way, so the tests are self-consistent at 6,960 and never see the launch rate.
The paths that derive a value from the live minimum, rather than sizing an account with it:
| Site | What it derives | Behaviour if rent moves under a parked account |
|---|---|---|
transfer_message_funds in mail_program’s delete handler | sender refund = live rent + fee + bounty; the rest is postage | a cut moves the sender’s surplus rent to the recipient; an increase is capped by .min(total) |
fund_message in mail_program’s send handler | one stamp = (frombox balance − live rent) ÷ stamps | a cut inflates every remaining stamp; an increase fails the checked subtraction until a top-up |
the frombox reclaim handler | residual = balance − live rent | a cut returns the surplus to the sender; an increase fails the checked subtraction |
the postoffice withdraw handler | revenue = balance − live rent | a cut sweeps the surplus as revenue (harmless) |
the reclaim-feature admin-close guards in all three programs | lamports <= live minimum means “no escrow” | a cut makes every pre-step account look like it holds escrow (AdminCloseEscrowPresent) |
Findings for a post-step-5 launch
F1 Host-side quotes read the cluster’s rent schedule
Resolved (v0.137.0). Every host-side quote reads the Rent sysvar once per
session and prices with solana_common’s _at forms: the CLI wizard
(live_rent → WizardActions::rent_schedule), the campaign quote’s rent
legs (fetch_pricing_context), and the web onboarding figure (readRent →
required_mailbox_funding_for_handle_at); a sysvar that cannot be read
falls back to Rent::default() with a stderr warning. The mailbox row of
the table above sizes Mailbox at 77 bytes (borsh::object_length of
Mailbox::default(), counting the no_ipfs and funder fields), and
src/images/onboarding-wizard-flow.svg’s funding-step text names the
wizard’s live-priced rent-only quote rather than a fixed SOL figure — the
self-alias it prices carries no ALIAS_FEE_LAMPORTS charge under
Self-alias waiver.
F2 Record the deposited rent in the Email and Frombox accounts
Delivered. Email and Frombox carry an appended rent_lamports field,
0 from Email::new/Frombox::new until the program records it — the
pattern Mailbox’s funder field set. SendMailContext::invoke_save_message
records the rent-exempt minimum it deposits into Email::rent_lamports,
overwriting whatever the SendMail instruction payload carried, and
CreateFromContext::invoke_create_from_account records the frombox’s the
same way. The three settlement legs read the recorded value instead of the
live sysvar: DeleteMailContext::transfer_message_funds’s sender refund,
SendMailContext::fund_message’s stamp-funding floor, and
ReclaimFromboxStampsContext::reclaim_frombox_stamps’s residual. Sizing
reads — allocation, the resize top-ups — and the postoffice withdraw
sweep keep reading the live Rent sysvar, which is correct for them.
There is no on-chain migration, the choice Mailbox’s funder append
already made: the host readers (mail_client’s and mail_grpc’s
read_account_data, each bound on the account type’s own
TryFrom<&[u8]> impl) read a pre-append account with rent_lamports at
0, while the program’s AccountViewExtensions::read_account_data (plain
BorshDeserialize, not the tolerant TryFrom) refuses one — the buffer
is short by the appended field’s 8 bytes. The next devnet deploy is
therefore a reset for existing message and frombox accounts, not an
upgrade they carry forward.
Residual: after a rent increase, a last-stamp SendMailContext::fund_message
and a ReclaimFromboxStampsContext::reclaim_frombox_stamps both leave the
frombox holding exactly its recorded deposit, which by then sits below
the live minimum; the runtime refuses the transaction
(InsufficientFundsForRent) rather than let the balance drop further.
The recipient’s CloseFromboxContext::close_frombox is the remaining
exit — it moves the account’s full balance to zero rather than a
proportional amount, so the shortfall doesn’t block it.
F3 Program tests run at the SDK default schedule
The settlement reads F2 added are fenced at today’s SDK-default test
schedule by mail_program/tests/settlement.rs’s seeded-delta tests —
RECORDED_RENT_DELTA, reclaim_settles_against_the_recorded_rent,
fund_message_settles_against_the_recorded_rent,
delete_mail_refunds_the_recorded_rent_above_the_live_minimum — each of
which seeds an account whose recorded rent_lamports sits
RECORDED_RENT_DELTA above the live minimum and goes red alone when the
settlement read it covers reverts to the live sysvar. What stays open for
this suite is a non-default rate: solana-program-test’s genesis pins
Rent::default(); ProgramTestContext’s set_sysvar changes it after boot
and ProgramTest’s add_sysvar_account stores one before it, and none of
settlement.rs’s tests call either. Standing up a suite at a non-default
rate is left open for the settlement reads rather than added here; each
program’s tests/admin_close.rs does call ProgramTestContext::set_sysvar
at a non-default rate (F5),
but that fences the admin-close guard, not the settlement reads this
finding covers.
F4 Docs carry fixed lamport figures
Resolved (v0.136.1). The economics page names each account class and
points at Rent per SithBit account class
instead of quoting a figure; the campaigns page keeps its sithbit campaign quote capture whole and states beside it that the rent legs are priced at
the SDK default schedule while the chain charges the live sysvar.
F5 The reclaim admin-close guard misreads pre-step accounts (devnet)
Resolved. admin_close_minimum_balance in program_common floors the
admin-close guard at the higher of the live Rent-sysvar minimum and
sdk_default_minimum_account_balance (pinocchio’s ACCOUNT_STORAGE_OVERHEAD
and DEFAULT_LAMPORTS_PER_BYTE constants), via program_common::rent_floor.
Each program’s AdminCloseAccountContext::verify_no_escrow — mail_program,
alias_program, domain_program all define the type under that name in
their own processor module — calls it instead of comparing against the
live minimum alone, so an account funded at the SDK default before a
downward rent step still reads as rent-only rather than escrow. Each
program’s tests/admin_close.rs fences both directions with
ProgramTestContext::set_sysvar: a downward step
(STEPPED_DOWN_LAMPORTS_PER_BYTE, the SIMD-0437-2 rate this page’s own
table names) closes a target funded at the SDK default and still refuses one
lamport above it, and an upward step (RAISED_LAMPORTS_PER_BYTE, a
hypothetical rate above the SDK default) closes a target funded at the live
minimum and still refuses one lamport above that raised live minimum.
F6 Waive the claim fee for a wallet’s own-address alias
Resolved (v0.138.0). fee_waived in alias_program’s create handler
(alias_program/src/processor/create.rs) admits a payer registering its
own base58 address alongside the standing delegate, so the CLI’s
mailbox create bundled CreateAlias costs rent only — no
Postoffice::alias_claim_fee. The same predicate closes the companion
vector: is_address_shaped (mail_model/src/instruction/alias.rs)
reserves 32-to-44-character lowercased base58 strings for the wallet they
name, and CreateAlias, AcceptTransferAlias, BuyAlias, SettleAuction
and the ListAlias/SellAlias staging refusal all raise
SithBitError::AddressAliasNotOwner (error 109) rather than let one land
anywhere else. Postoffice revenue forgoes the fee per self-alias, consistent
with the existing delegate waiver’s rationale. See
Self-alias waiver.
F7 Sponsored mailbox creation: exposure and guards
What verify_sponsor in the mail program’s create handler enforces today:
only the domain’s on-chain authority key may pay for another owner’s
mailbox; a sponsored mailbox’s postage is forced to DEFAULT_POSTAGE_LAMPORTS;
no self-alias is bundled. No server in this tree exposes sponsored
creation (the account API’s create-mailbox route is an IMAP folder
operation), so sponsorship is CLI-only.
The gap is capital stranding, not spam. The sponsor is the recorded
funder, but only the owner can request and finalize a close, behind
MAILBOX_CLOSE_TIMELOCK_SECS. A requester who reaches a sponsorship
endpoint generates wallets for free and makes the operator pay rent plus
one signature per junk mailbox with no way to get it back. Captchas and
human-like-flow checks do not hold against agents; what holds is anything
that bounds the operator’s loss or costs the requester something it cannot
mint. See Sponsorship guards for the mechanism.
| Exposure per junk mailbox at 696 | lamports | approx. USD |
|---|---|---|
| mailbox rent + one signature (no alias) | 147,680 | $0.012 |
| with the owner’s self-alias bundled | 259,040 | $0.020 |
USD figures use the SOL price the upgrade page implies (a token account at $0.0159 puts SOL near $78).
F8 Not needed: a withdraw-excess-rent instruction
Under the launch assumption no mainnet account ever holds surplus, so an instruction returning excess to the funder is not a launch item. It only matters if a future increase is followed by another decrease.
Adjacent: v1 transactions
The same Agave 4.2 release raises the transaction envelope to 4096 bytes.
The MAX_PROOF_WITNESS_LEN comment records that SithBit still emits legacy
transactions, which is why a DNSSEC chain is staged through a chunked
witness buffer. Adopting v1 transactions is a separate item, not rent.
Self-create cost with and without the waiver
A default self-create is mailbox rent + alias rent + the claim fee (unless
waived) + one signature; the wizard adds FUNDING_BUFFER_LAMPORTS on top.
A sponsored create skips the alias.
| Schedule | mailbox rent | alias rent | claim fee | signature | total (fee) | total (waived) |
|---|---|---|---|---|---|---|
| 6,960 (SDK default) | 1,426,800 | 1,113,600 | 10,000,000 | 5,000 | 12,545,400 | 2,545,400 |
| 5,080 (live now) | 1,041,400 | 812,800 | 10,000,000 | 5,000 | 11,859,200 | 1,859,200 |
| 696 (final) | 142,680 | 111,360 | 10,000,000 | 5,000 | 10,259,040 | 259,040 |
Implementation notes
Self-alias waiver
Resolved (v0.138.0).
- The instruction carries the alias already lowercased (
CreateAliasArgs), and the PDA seeds on the blake3 of that string, so the program cannot decode the alias back to a key. Base58 is case-sensitive: the check base58-encodes the signing payer, lowercases the encoding, and compares it to the alias. A lowercase collision between two valid keys is negligible but the comparison direction matters. fee_waivedinalias_program’s create handler (alias_program/src/processor/create.rs) admits the standing delegate or a payer registering its own address —own_address, decided once byverify_address_alias_claim’sencodes_own_addresscall and never re-derived. The encode runs only when the alias length falls in the base58 pubkey range, in its own#[inline(never)]helper for the SBF stack-frame cap the program crates document.- The predicate lives in
mail_model::instruction::alias(is_address_shaped,is_own_address_alias), so the program,required_mailbox_funding_for_aliasinsolana_commonandmail_wasmread one definition, fenced by thesolana_commonparity tests and the setup-wizard funding test inmail_client. - The squat guard is the same predicate negated:
verify_address_alias_claimrefuses any address-shaped alias whose payer isn’t the wallet it names, onCreateAlias,AcceptTransferAlias,BuyAlias,SettleAuction, and theListAlias/SellAliasstaging refusal. - The waiver keys on the payer, not the mailbox owner:
fee_waived’sown_addressreads the signer alone, so a sponsored self-alias, if bundled, still pays the fee under this predicate. Bundling a sponsored self-alias fee-free is F7’s item, not this one. - Change-history class: MINOR (an economic-model change), landed with the create-alias, create-mailbox and economics pages in the same change.
Recorded rent
EmailandFromboxgain an appendedrent_lamportsfield, 0 fromEmail::new/Frombox::new;SendMailContext::invoke_save_messageandCreateFromContext::invoke_create_from_accountrecord the live minimum into it at creation. The pre-append account still decodes (rent_lamportsreads back 0) on the host side, the way the mailboxfunderappend does, but the program’s ownAccountViewExtensions::read_account_datarejects a pre-append buffer outright, so there is no on-chain migration path — existing message and frombox accounts reset at the next devnet deploy.DeleteMailContext::transfer_message_fundsrefunds the recorded value;SendMailContext::fund_messageandReclaimFromboxStampsContext::reclaim_frombox_stampssubtract the recorded value; the postofficewithdrawsweep and the resize top-ups keep reading the live sysvar, which is correct for them.mail_program/tests/settlement.rs’s seeded-delta tests (reclaim_settles_against_the_recorded_rent,fund_message_settles_against_the_recorded_rent,delete_mail_refunds_the_recorded_rent_above_the_live_minimum) fence each settlement read at today’s SDK-default schedule; each goes red alone when its read reverts to the live sysvar. A suite at a non-default rate is open —solana-program-testgenesis pinsRent::default();ProgramTestContext’sset_sysvarandProgramTest’sadd_sysvar_accountare the untried ways to move it.
Sponsorship guards
- Funder dormancy reclaim (protocol). Append a creation timestamp and
an owner-activated flag to
Mailbox; any owner-signed instruction sets the flag. A new instruction lets the recordedfunderclose a sponsored mailbox whose flag is unset and whose mail count is zero after a grace window. This turns stranded rent into a recoverable deposit: an attack then costs the operator only signatures and the time value of the rent. - Per-domain cap and rate (protocol). Append a sponsored count, a cap
and a per-epoch counter to
MailDomain, settable by the authority and optionally bounded by the postmaster, checked inverify_sponsor. This ceiling survives an endpoint bug or a leaked key. - Sponsor delegate key (protocol). Today the payer must be the domain
authority itself, which forces that key onto whatever hot server does
onboarding. A recorded sponsor delegate on
MailDomainlets a hot, budget-limited key sponsor while the authority stays cold, and a leaked delegate is revoked without touching the domain. - Operator gating (no protocol change). Never expose a bare
“make me a mailbox” endpoint. Tie each request to the account API’s
wallet-challenge login so one token maps to one mailbox and limits apply
per key; gate behind an invite code or employer roster (the documented use
case) or a verified external identity; add a daily SOL budget with
alerting through
mail_observeand an automatic halt, and keep only that budget on the hot wallet. Proof-of-work only sets a price and is at most a secondary throttle.
Sources
- Reduced Rent — the upgrade
page (the
/upgrades/reduced-costURL answers 404). - Rent Reduction on Solana: A Data-Backed Analysis — the Solana Foundation’s state-growth analysis behind the proposal.
- SIMD-0437 step 2 on mainnet — activation record; states that reclaiming excess is not automatic.