SYSTEM PROGRAM — Solana × SIMD-0312

The End of "I Sent SOL to an Address That Doesn't Exist Yet"

6 min read
SolanaSIMD-0312System ProgramDeposits

It is the most common support ticket in crypto, on every chain, and on Solana it has a specific mechanical cause. Someone is given an address — a deposit address, a program-derived address, an account a dApp is about to create — and sends SOL to it before the account exists. The SOL arrives. The address now has a balance and no account behind it. And when the program that was supposed to create the account finally tries, the system program refuses, because you cannot create something that already holds lamports.

The funds are not lost. But getting them into a working account has required a workaround that most programs did not implement, and most users could not perform. SIMD-0312 replaces the workaround with one instruction.


Why Creating a Funded Address Failed

Solana's CreateAccount instruction does three things at once: allocates space, assigns an owner program, and transfers the initial lamports from a funder. It was written with a strict precondition: the target must be empty — zero lamports, zero data, owned by the system program. A target with a non-zero balance fails the check, and the instruction errors.

The precondition exists for a reason. It stops one party from pre-loading an account another party expects to initialise, and it makes the "fresh account" invariant something programs can rely on. But it also means the ordinary human act of sending money to an address slightly too early bricks the creation path.

The workaround was a three-instruction dance: Allocate space, Assign the owner, then Transfer the top-up. It works, it is fiddly, and it requires the creating program to anticipate the case. Most did not.


What SIMD-0312 Adds

A new system instruction, CreateAccountAllowPrefund, that does what its name says: creates the account whether or not the address already holds lamports. If the address has been prefunded, the existing balance counts toward the requested amount; the funder tops up only the difference, if any.

Two details from the implementation that anyone building on it needs:

Account order is reversednew account = index 0, funder = index 1
When requested lamports = 0only one account is required: the target

The reversal matters because it is the opposite of CreateAccount, and a transposed pair of accounts is the kind of mistake that compiles, passes a happy-path test, and fails on the first prefunded address in production. And when the address is already fully funded there is no funder to reference, because nothing is being transferred.

It is gated by a feature, create_account_allow_prefund, which activated on mainnet on 29 May 2026 (epoch 979, slot 422,928,004).


Who This Fixes, and Who It Does Not Fix By Itself

The instruction is a tool for programs and wallets, not a button a user presses. Nothing changes for a stranded deposit until the software that owns the address adopts it. So:

  • Deposit systems — exchanges, payment processors, anything that hands out addresses before creating accounts — can now create the account after the money arrives, in one instruction, with no special path. The pattern "give the customer an address, let them fund it, create-and-claim later" becomes cheap and safe.
  • Program-derived addresses — vaults, escrows, per-user state accounts — can be funded by a user before the program initialises them, and the program's init can succeed anyway.
  • Users with SOL sitting at an uncreated address are still dependent on whoever controls the creation path. The difference is that the fix on that side is now trivial, which is the strongest argument for it happening.

The Invariant That Changes

The "fresh account" guarantee weakens slightly. A program that uses CreateAccountAllowPrefund must be written to tolerate a target with a pre-existing balance — which is the whole point — and must not assume the lamports it sees were put there by its funder. For most programs that is harmless. For a program whose logic depends on the exact initial balance, it is a case to handle deliberately: read the balance after creation, not the amount you asked for.

The classic CreateAccount keeps its strict behaviour. Programs that want the old invariant keep using it.


What It Does Not Do

It does not recover SOL sent to a wrong address. If the address was mistyped, or belongs to someone else, or is an address nobody controls, the instruction is irrelevant; there is no account to create on your behalf.

It does not apply to token accounts. Associated token accounts have had their own tolerant creation path for a long time; this is about system-owned accounts and program-derived ones.

And it does not fire on its own. The gate has been active on mainnet since 29 May 2026, but until the program in question uses the instruction, a prefunded address is exactly as stuck as it was before.


Prefunded Addresses: Questions People Actually Ask

Why does Solana fail to create an account that already has SOL?

The classic CreateAccount instruction requires an empty target: zero lamports, zero data, system-owned. A balance sent early fails that check and the creation errors.

What does SIMD-0312 change?

It adds CreateAccountAllowPrefund, a system instruction that creates the account and counts any existing balance toward the requested amount, so the funder only tops up the difference.

Can I recover SOL I sent to an uncreated Solana address?

The SOL is at the address. Whoever controls creation of that address can now create the account in one instruction; if it is a seed-derived address under your own key, you can allocate and assign it yourself.

Does SIMD-0312 apply to token accounts?

No. Associated token accounts already tolerate prefunding through their own creation path. This instruction is for system-owned and program-derived accounts.


If You Have SOL at an Address That Does Not Exist

Find out who controls creation of that address. If it is a program, the fix is one instruction on their side, live on mainnet since 29 May 2026. If it is a seed-derived address under your own key, you can allocate and assign it yourself today, and the top-up is already there.

If you build software that hands out addresses: adopt the new instruction now; the gate has been live since 29 May 2026. The support tickets you stop receiving are the measure of the change.

— The Gate Is Live on Mainnet —

The instruction exists. The question is which cluster has it.

create_account_allow_prefund activated on mainnet at the epoch 979 boundary on 29 May 2026, after 95% of stake adopted it. The upgrades tracker reads the feature-gate accounts from the chain every minute and shows the state per cluster.

Open the Upgrades Tracker ↗

Two neighbours to this problem: SOL that vanishes en route rather than on arrival is a different failure with seven causes, and SOL sitting in accounts you already control is the rent surplus left by the rent cut.

Instruction behaviour read from the Agave validator source at commit beee69b958: programs/system/src/system_processor.rs and feature-set/src/lib.rs.

xroot.devOn-chain research, published in full
𝕏 @xrootdev
← Back to all notes