MagicBlock: ephemeral rollups for real-time Solana, 10ms blocks and all
MagicBlock's ephemeral rollups extend Solana with 10ms block times via delegated accounts. Here's the architecture, the delegation flow, and when to use it.
Solana ships ~400ms slots — fast for L1, slow for real-time gaming or in-fight order updates in a perpetual exchange. MagicBlock is the protocol that gives Solana ~10ms block times when needed, via a primitive called ephemeral rollups (ERs).
Critically, it's not a separate L2. It's a temporary delegation layer that runs your existing Solana accounts in a faster runtime, then commits state back to mainnet when you're done.
The delegation flow
1. Account lives on Solana mainnet (owned by your program)
2. You call MagicBlock's delegation_program to "delegate" the account
to an ephemeral rollup
↓
The mainnet account becomes write-locked
A copy of the account state appears in the ER
3. Inside the ER, the same program logic runs against the account
at ~10ms block times, free of transaction fees
Many txs per second update the account state
4. When the session ends, MagicBlock posts the final account state
back to mainnet via a commit transaction
↓
The mainnet account is unlocked + updatedThe user's perspective: connect wallet, play / trade / move at console-tier latency, then settle to L1 when finished. The program's perspective: the same code runs in both places without changes — MagicBlock's runtime is bytecode- compatible with Solana's SVM.
What makes it ephemeral
The ER doesn't have persistent state of its own. It exists only while accounts are delegated to it. When all delegations end, the ER goes idle — the canonical state lives on Solana mainnet at all times. No bridges, no fragmented liquidity, no cross-chain risk.
Compared to a traditional rollup (Arbitrum/Optimism-style), this is a fundamentally different architecture:
Traditional rollup MagicBlock ER
────────────────────────────────────────────────────────────────
State Lives in the rollup Lives on Solana
Bridge required Yes (deposit/withdraw) No (delegate/commit)
Canonical chain Rollup, until withdrawn Always Solana
Block time ~1-12s (varies) ~10ms
Compatibility EVM (different runtime) SVM (same runtime)
Delegation Long-lived (forever) Per-session, returnableThe delegation_program
// Inside your program, mark an account as delegatable
use ephemeral_rollups_sdk::cpi::delegate_account;
use ephemeral_rollups_sdk::ephem::commit_and_undelegate_accounts;
#[derive(Accounts)]
pub struct Delegate<'info> {
#[account(mut)]
pub payer: Signer<'info>,
/// CHECK: the account you're delegating
#[account(mut)]
pub pda: AccountInfo<'info>,
/// CHECK: validated by SDK
pub owner_program: AccountInfo<'info>,
/// CHECK: validated by SDK
pub buffer: AccountInfo<'info>,
/// CHECK: validated by SDK
pub delegation_record: AccountInfo<'info>,
/// CHECK: validated by SDK
pub delegation_metadata: AccountInfo<'info>,
/// CHECK: validated by SDK
pub delegation_program: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}
pub fn handle_delegate(ctx: Context<Delegate>, pda_seeds: Vec<Vec<u8>>) -> Result<()> {
delegate_account(
&ctx.accounts.payer,
&ctx.accounts.pda,
&ctx.accounts.owner_program,
&ctx.accounts.buffer,
&ctx.accounts.delegation_record,
&ctx.accounts.delegation_metadata,
&ctx.accounts.delegation_program,
&ctx.accounts.system_program,
pda_seeds,
/* commit_frequency_ms */ 30_000,
/* validator */ None,
)?;
Ok(())
}
// Inside the ER, run as many updates as you want:
// update_my_state(...) // ~10ms confirmation, free
// When done, undelegate to push state back to mainnet:
pub fn handle_undelegate(ctx: Context<Undelegate>) -> Result<()> {
commit_and_undelegate_accounts(
&ctx.accounts.payer,
vec![&ctx.accounts.pda],
&ctx.accounts.magic_context,
&ctx.accounts.magic_program,
)?;
Ok(())
}What this is good for
- Real-time games. An FPS or RTS where position updates need to round-trip in <100ms. Delegate per-match state, settle the result to L1 at end-of-match.
- Order books on perpetual DEXes. Place, modify, cancel at human-typing speed without paying gas per action. Settle when positions close.
- High-frequency on-chain state. Live auctions, rapid bidding, real-time leaderboards.
- Account abstraction-style flows. Group many user actions into one mainnet commit, dramatically reducing per-action cost and friction.
What it's not good for
- Long-lived state that needs L1 finality at all times (treasury balances, governance) — delegate locks the account; the L1 version becomes a snapshot until you undelegate.
- Cross-program interactions with non-delegated programs from inside the ER. The ER can only see and write delegated accounts; CPI into unrelated programs needs to come back to L1.
- Use cases where 400ms is already fast enough. Don't add complexity for nothing.
What ships in the SDK
cargo add ephemeral-rollups-sdk
# or for client-side:
npm install @magicblock-labs/ephemeral-rollups-sdkTwo ergonomic helpers: the Rust SDK gives you delegate_account and commit_and_undelegate macros for your program; the TypeScript SDK gives you the client-side flow to route transactions to the ER endpoint instead of mainnet.
References
MagicBlock isn't an L2 — it's "mainnet, but with a temporary fast lane." If your app needs sub-100ms feedback loops and your state still lives canonically on Solana, this is the only mechanism that gives you both.