Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

StepGate (prefix)lamports/bytedevnetmainnet-beta
SIMD-0437-14a6f7o7i…6,333activeactive
SIMD-0437-261BtM7Bk…5,080activeactive
SIMD-0437-3Ftxb3ZKq…2,575not yet allowednot yet allowed
SIMD-0437-4GsUBNYND…1,322not yet allowednot yet allowed
SIMD-0437-5mZdnRh9T…696not yet allowednot 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).

Accountdata bytesat 6,960at 5,080at 696
message (Email, 46-byte CID)1782,129,7601,554,480212,976
frombox (Frombox, default instance)201,030,080751,840103,008
sender reputation (SenderReputation)401,169,280853,440116,928
mailbox (Mailbox, default instance)771,426,8001,041,400142,680
alias (Alias)321,113,600812,800111,360
participant beacon (PARTICIPANT_BEACON_LEN)1451,900,0801,386,840190,008
pin lease (PIN_LEASE_LEN)721,392,0001,016,000139,200
alias listing (ALIAS_LISTING_LEN)1051,621,6801,183,640162,168
encryption key (MAX_ENCRYPTION_KEY_LENGTH key)5164,482,2403,271,520448,224
proof witness (MAX_PROOF_WITNESS_LEN + header)8,26458,408,32042,631,3605,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_balance in program_common reads Rent::get(), the live sysvar. Every CreateAccount and every resize top-up in the programs sizes from it, so they are correct at any step.
  • Host-side: get_minimum_account_balance in solana_common reads Rent::default(), the SDK constant. Its callers are required_mailbox_funding_for_alias (the CLI setup wizard and, through required_mailbox_funding_for_handle in mail_wasm, the web onboarding figure) and the rent legs of mail_client’s campaign pricing. The pin-lease command and the devnet integration tests fetch the live value over RPC.
  • Program tests: solana-program-test genesis carries the SDK default, and the suites’ rent_for helper 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:

SiteWhat it derivesBehaviour if rent moves under a parked account
transfer_message_funds in mail_program’s delete handlersender refund = live rent + fee + bounty; the rest is postagea cut moves the sender’s surplus rent to the recipient; an increase is capped by .min(total)
fund_message in mail_program’s send handlerone stamp = (frombox balance − live rent) ÷ stampsa cut inflates every remaining stamp; an increase fails the checked subtraction until a top-up
the frombox reclaim handlerresidual = balance − live renta cut returns the surplus to the sender; an increase fails the checked subtraction
the postoffice withdraw handlerrevenue = balance − live renta cut sweeps the surplus as revenue (harmless)
the reclaim-feature admin-close guards in all three programslamports <= 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 696lamportsapprox. USD
mailbox rent + one signature (no alias)147,680$0.012
with the owner’s self-alias bundled259,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.

Schedulemailbox rentalias rentclaim feesignaturetotal (fee)total (waived)
6,960 (SDK default)1,426,8001,113,60010,000,0005,00012,545,4002,545,400
5,080 (live now)1,041,400812,80010,000,0005,00011,859,2001,859,200
696 (final)142,680111,36010,000,0005,00010,259,040259,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_waived in alias_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 by verify_address_alias_claim’s encodes_own_address call 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_alias in solana_common and mail_wasm read one definition, fenced by the solana_common parity tests and the setup-wizard funding test in mail_client.
  • The squat guard is the same predicate negated: verify_address_alias_claim refuses any address-shaped alias whose payer isn’t the wallet it names, on CreateAlias, AcceptTransferAlias, BuyAlias, SettleAuction, and the ListAlias/SellAlias staging refusal.
  • The waiver keys on the payer, not the mailbox owner: fee_waived’s own_address reads 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

  • Email and Frombox gain an appended rent_lamports field, 0 from Email::new/Frombox::new; SendMailContext::invoke_save_message and CreateFromContext::invoke_create_from_account record the live minimum into it at creation. The pre-append account still decodes (rent_lamports reads back 0) on the host side, the way the mailbox funder append does, but the program’s own AccountViewExtensions::read_account_data rejects 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_funds refunds the recorded value; SendMailContext::fund_message and ReclaimFromboxStampsContext::reclaim_frombox_stamps subtract the recorded value; the postoffice withdraw sweep 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-test genesis pins Rent::default(); ProgramTestContext’s set_sysvar and ProgramTest’s add_sysvar_account are 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 recorded funder close 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 in verify_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 MailDomain lets 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_observe and 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