Comparing magicblock with surfpool

magicblock

View full →

Author

@0xinit

Stars

53

Repository

0xinit/cryptoskills

skills/magicblock/SKILL.md

MagicBlock Ephemeral Rollups Guide

A comprehensive guide for building high-performance Solana applications with MagicBlock Ephemeral Rollups - enabling sub-10ms latency and gasless transactions.

Overview

MagicBlock Ephemeral Rollups (ER) are specialized SVM runtimes that enhance Solana with:

  • Sub-10ms latency (vs ~400ms on base Solana)
  • Gasless transactions for seamless UX
  • Full composability with existing Solana programs
  • Horizontal scaling via on-demand rollups

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Your Application                        │
├─────────────────────────────────────────────────────────────┤
│  Base Layer (Solana)          │  Ephemeral Rollup (ER)      │
│  - Initialize accounts        │  - Execute operations       │
│  - Delegate accounts          │  - Process at ~10-50ms      │
│  - Final state commits        │  - Zero gas fees            │
│  - ~400ms finality            │  - Commit state to Solana   │
└─────────────────────────────────────────────────────────────┘

Core Flow

  1. Initialize - Create accounts on Solana base layer
  2. Delegate - Transfer account ownership to delegation program
  3. Execute - Run fast operations on Ephemeral Rollup
  4. Commit - Sync state back to base layer
  5. Undelegate - Return ownership to your program

Prerequisites

# Required versions
Solana: 2.3.13
Rust: 1.85.0
Anchor: 0.32.1
Node: 24.10.0

# Install Anchor (if needed)
cargo install --git https://github.com/coral-xyz/anchor anchor-cli

Quick Start

1. Add Dependencies (Cargo.toml)

[dependencies]
anchor-lang = "0.32.1"
ephemeral-rollups-sdk = { version = "0.6.5", features = ["anchor", "disable-realloc"] }

2. Program Setup (lib.rs)

use anchor_lang::prelude::*;
use ephemeral_rollups_sdk::anchor::{delegate_account, commit_accounts, ephemeral};
use ephemeral_rollups_sdk::cpi::DelegationProgram;

declare_id!("YourProgramId111111111111111111111111111111");

#[ephemeral]  // Required: enables ER support
#[program]
pub mod my_program {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        ctx.accounts.state.value = 0;
        Ok(())
    }

    #[delegate]  // Auto-injects delegation accounts
    pub fn delegate(ctx: Context<Delegate>) -> Result<()> {
        Ok(())
    }

    pub fn increment(ctx: Context<Update>) -> Result<()> {
        ctx.accounts.state.value += 1;
        Ok(())
    }

    #[commit]  // Auto-injects commit accounts
    pub fn undelegate(ctx: Context<Undelegate>) -> Result<()> {
        Ok(())
    }
}

3. TypeScript Client Setup

import { Connection, PublicKey } from "@solana/web3.js";
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import { DELEGATION_PROGRAM_ID } from "@magicblock-labs/ephemeral-rollups-sdk";

// CRITICAL: Separate connections for each layer
const baseConnection = new Connection("https://api.devnet.solana.com");
const erConnection = new Connection("https://devnet.magicblock.app");

// Create providers
const baseProvider = new AnchorProvider(baseConnection, wallet, { commitment: "confirmed" });
const erProvider = new AnchorProvider(erConnection, wallet, {
  commitment: "confirmed",
  skipPreflight: true,  // Required for ER
});

// Check delegation status
async function isDelegated(pubkey: PublicKey): Promise<boolean> {
  const info = await baseConnection.getAccountInfo(pubkey);
  return info?.owner.equals(DELEGATION_PROGRAM_ID) ?? false;
}

Key Concepts

Delegation

Delegation transfers PDA ownership to the delegation program, allowing the Ephemeral Validator to process transactions.

#[derive(Accounts)]
pub struct Delegate<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    /// CHECK: Will be delegated
    #[account(mut, del)]  // 'del' marks for delegation
    pub state: AccountInfo<'info>,
    pub delegation_program: Program<'info, DelegationProgram>,
}

Commit

Commits update PDA state from ER to base layer without undelegating.

use ephemeral_rollups_sdk::anchor::commit_accounts;

pub fn commit(ctx: Context<Commit>) -> Result<()> {
    commit_accounts(
        &ctx.accounts.payer,
        vec![&ctx.accounts.state.to_account_info()],
        &ctx.accounts.magic_context,
        &ctx.accounts.magic_program,
    )?;
    Ok(())
}

Undelegation

Returns PDA ownership to your program while committing final state.

#[commit]  // Handles commit + undelegate
pub fn undelegate(ctx: Context<Undelegate>) -> Result<()> {
    Ok(())
}

ER Validators (Devnet)

RegionValidator Identity
AsiaMAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
EUMEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
USMUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
TEEFnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA

Magic Router (auto-selects best): https://devnet-router.magicblock.app

Critical Rules

DO:

  • Maintain separate connections for base layer and ER
  • Use skipPreflight: true for all ER transactions
  • Verify delegation status before sending to ER
  • Use AccountInfo for delegated accounts in Rust
  • Match PDA seeds exactly between Rust and TypeScript

DON'T:

  • Send delegated account operations to base layer
  • Mix base layer and ER operations in single transaction
  • Assume account ownership without checking
  • Skip commitment verification before base layer reads

Products

ProductDescription
Ephemeral Rollup (ER)High-performance, gasless transactions
Private ER (PER)Privacy-preserving computation with Intel TDX
VRFVerifiable random function for on-chain randomness
BOLT FrameworkECS architecture for fully on-chain games
Solana PluginsApp-specific extensions for enhanced capabilities

Solana Plugins (New)

Solana Plugins are modular capabilities that can be added to your dApp to extend what's possible on Solana. Think of them as your custom toolkit: plug in what you need, when you need it.

Available Plugins

PluginDescriptionUse Cases
Verifiable Randomness (VRF)Provably fair on-chain randomnessGames, lotteries, NFT drops
Real-Time Price FeedsUp-to-the-millisecond market dataDEXs, trading bots, DeFi
AI OraclesCall AI models directly from smart contractsDynamic NFTs, AI agents

Using VRF Plugin

import { requestRandomness, getRandomnessResult } from "@magicblock-labs/vrf-sdk";

// Request randomness
const requestTx = await requestRandomness({
  payer: wallet.publicKey,
  seed: Buffer.from("my_game_seed"),
});

// Get result after confirmation
const result = await getRandomnessResult(requestId);
console.log("Random value:", result.randomness);

Privacy with Intel TDX

MagicBlock enables privacy in any Solana program state account through Ephemeral Rollups running in Trusted Execution Environments (TEE) on Intel TDX. This allows:

  • Private computation without exposing state
  • Verifiable execution guarantees
  • Selective disclosure of results

Resources

Skill Structure

magicblock/
├── SKILL.md                          # This file
├── resources/
│   ├── api-reference.md              # Complete API reference
│   └── program-ids.md                # All program IDs and constants
├── examples/
│   ├── anchor-counter/README.md      # Basic counter with delegation
│   ├── delegation-flow/README.md     # Full delegation lifecycle
│   ├── vrf-randomness/README.md      # VRF integration
│   └── crank-automation/README.md    # Scheduled tasks
├── templates/
│   ├── program-template.rs           # Rust program starter
│   └── client-template.ts            # TypeScript client starter
└── docs/
    ├── advanced-patterns.md          # Complex patterns
    └── troubleshooting.md            # Common issues

Author

@0xinit

Stars

53

Repository

0xinit/cryptoskills

skills/surfpool/SKILL.md

Surfpool - Solana Development Environment

The definitive guide for Surfpool - where developers start their Solana journey. A drop-in replacement for solana-test-validator that enables local program simulation using Mainnet accounts fetched just-in-time.

What is Surfpool?

Surfpool is a comprehensive development environment that combines local-first testing with real Mainnet data access:

  • Mainnet Forking - Clone accounts, programs, and token balances from Mainnet instantly
  • Cheatcodes - Special RPC methods for time travel, balance manipulation, and state control
  • Infrastructure as Code - Reproducible, auditable deployments using txtx DSL
  • Surfpool Studio - Embedded dashboard with transaction inspection and profiling
  • Universal Faucet - Get SOL, USDC, USDT, BONK from a single interface

Key Benefits

FeatureDescription
Instant BootNo 2TB snapshots, runs on Raspberry Pi
Lazy ForkingCopy-on-read strategy pulls mainnet data as needed
Full CompatibilityWorks with solana-cli, Anchor, wallets, explorers
Zero ConfigAuto-detects Anchor projects and deploys programs

Statistics

  • 460+ GitHub stars
  • 100+ forks
  • Apache 2.0 license
  • Current version: v1.0.0

Installation

Automated Installer (Recommended)

curl -sL https://run.surfpool.run/ | bash

Homebrew (macOS)

brew install txtx/taps/surfpool

From Source

git clone https://github.com/txtx/surfpool.git
cd surfpool
cargo surfpool-install

Docker

docker pull surfpool/surfpool
docker run -p 8899:8899 -p 18488:18488 surfpool/surfpool

Quick Start

Start Local Network

# Start with default configuration
surfpool start

# Start with custom RPC source
surfpool start -u https://api.mainnet-beta.solana.com

# Start without terminal UI
surfpool start --no-tui

# Start with debug logging
surfpool start --debug

Access Points

ServiceURLDescription
RPC Endpointhttp://127.0.0.1:8899Standard Solana RPC
WebSocketws://127.0.0.1:8900Real-time subscriptions
Surfpool Studiohttp://127.0.0.1:18488Web dashboard

CLI Commands

surfpool start

Start the local Surfnet network.

surfpool start [OPTIONS]

Options:

OptionDefaultDescription
-m, --manifest-file-path./Surfpool.tomlPath to manifest file
-p, --port8899RPC port
-o, --host127.0.0.1Host address
-s, --slot-time400Slot time in ms
-u, --rpc-urlhttps://api.mainnet-beta.solana.comSource RPC URL
--no-tui-Disable terminal UI
--debug-Enable debug logs
--no-deploy-Disable auto deployments
-r, --runbookdeploymentRunbooks to execute
-a, --airdrop-Pubkeys to airdrop
-q, --airdrop-amount10000000000000Airdrop amount (lamports)
-k, --airdrop-keypair-path-Keypair path for airdrop
--no-explorer-Disable explorer

Example Usage

# Start with airdrop to specific address
surfpool start -a YOUR_PUBKEY -q 100000000000

# Start with custom slot time (faster blocks)
surfpool start -s 100

# Start with specific runbook
surfpool start -r deployment -r setup

Surfpool.toml Configuration

Create a Surfpool.toml in your project root:

[network]
slot_time = 400
epoch_duration = 432000
rpc_url = "https://api.mainnet-beta.solana.com"

[behavior]
# Fork from mainnet genesis
genesis = false
# Fork from specific point
point_fork = true

[accounts]
# Pre-clone specific accounts
clone = [
  "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",  # Token Program
  "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", # ATA Program
]

[programs]
# Auto-deploy local programs
deploy = ["./target/deploy/my_program.so"]

[airdrop]
# Default airdrop recipients
addresses = ["YOUR_PUBKEY"]
amount = 10000000000000  # 10,000 SOL

Cheatcodes

Surfpool provides special RPC methods for advanced state manipulation during testing.

Account Manipulation

surfnet_setAccount

Set arbitrary account data:

await connection.send("surfnet_setAccount", [
  {
    pubkey: "AccountPubkey...",
    lamports: 1000000000,
    data: "base64EncodedData",
    owner: "OwnerPubkey...",
    executable: false,
  },
]);

surfnet_setTokenAccount

Create or modify token accounts:

await connection.send("surfnet_setTokenAccount", [
  {
    owner: "OwnerPubkey...",
    mint: "MintPubkey...",
    tokenProgram: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    update: {
      amount: "1000000000",
      delegate: null,
      state: "initialized",
    },
  },
]);

surfnet_cloneProgramAccount

Clone a program from mainnet:

await connection.send("surfnet_cloneProgramAccount", [
  {
    source: "SourceProgramPubkey...",
    destination: "DestinationPubkey...",
  },
]);

surfnet_resetAccount

Reset account to mainnet state:

await connection.send("surfnet_resetAccount", [
  {
    pubkey: "AccountPubkey...",
    includeOwnedAccounts: true,
  },
]);

Time Control

surfnet_timeTravel

Advance network time:

await connection.send("surfnet_timeTravel", [
  {
    epoch: 100,
    slot: 50000,
    timestamp: 1700000000,
  },
]);

surfnet_pauseClock / surfnet_resumeClock

Control block production:

// Pause
await connection.send("surfnet_pauseClock", []);

// Resume
await connection.send("surfnet_resumeClock", []);

surfnet_advanceClock

Advance clock incrementally:

await connection.send("surfnet_advanceClock", [
  { slots: 100 },
]);

Transaction Profiling

surfnet_profileTransaction

Profile transaction execution:

const result = await connection.send("surfnet_profileTransaction", [
  {
    transaction: "base64EncodedTx",
    tag: "my-test-tag",
  },
]);

console.log("Compute units:", result.computeUnits);
console.log("Account changes:", result.accountChanges);

surfnet_getProfileResults

Get profiling results by tag:

const results = await connection.send("surfnet_getProfileResults", [
  { tag: "my-test-tag" },
]);

Network Control

surfnet_resetNetwork

Reset entire network to initial state:

await connection.send("surfnet_resetNetwork", []);

surfnet_getClock

Get current network time:

const clock = await connection.send("surfnet_getClock", []);
console.log("Slot:", clock.slot);
console.log("Epoch:", clock.epoch);
console.log("Timestamp:", clock.timestamp);

Surfpool Studio

Access the web dashboard at http://127.0.0.1:18488 for:

  • Transaction Inspector - View transaction details with byte-level diffs
  • Account Browser - Explore account state and history
  • Compute Profiler - Analyze compute unit usage per instruction
  • Universal Faucet - Request SOL and tokens
  • Network Status - Monitor slots, epochs, and block production

Infrastructure as Code

Surfpool integrates txtx DSL for reproducible deployments.

Runbook Structure

# deployment.tx

// Define signers
signer "deployer" "svm::secret_key" {
  secret_key = env.DEPLOYER_KEY
}

// Deploy program
action "deploy_program" "svm::deploy_program" {
  program_path = "./target/deploy/my_program.so"
  signer = signer.deployer
}

// Initialize program
action "initialize" "svm::send_transaction" {
  transaction {
    instruction {
      program_id = action.deploy_program.program_id
      data = encode_instruction("initialize", {})
    }
  }
  signers = [signer.deployer]
}

Running Runbooks

# Run specific runbook
surfpool start -r deployment

# Run in unsupervised mode
surfpool start -r deployment --unsupervised

Scenarios and Fixtures

Scenarios

Define account state sequences for testing:

await connection.send("surfnet_registerScenario", [
  {
    name: "high-volume-trading",
    slots: [
      {
        slot: 100,
        accounts: {
          "PoolPubkey...": { lamports: 1000000000000 },
        },
      },
      {
        slot: 200,
        accounts: {
          "PoolPubkey...": { lamports: 500000000000 },
        },
      },
    ],
  },
]);

Fixtures

Export transaction fixtures for reproducible tests:

const fixture = await connection.send("surfnet_exportSnapshot", [
  {
    transaction: "txSignature...",
    format: "json",
  },
]);

// Save fixture for CI/CD
fs.writeFileSync("fixtures/my-test.json", JSON.stringify(fixture));

Integration with Anchor

Surfpool auto-detects Anchor projects and handles deployment:

# In an Anchor project directory
surfpool start
# Programs in target/deploy/ are automatically deployed

Testing with Anchor

import * as anchor from "@coral-xyz/anchor";

describe("My Program", () => {
  // Use local Surfnet
  const provider = anchor.AnchorProvider.local("http://127.0.0.1:8899");
  anchor.setProvider(provider);

  it("works with mainnet state", async () => {
    // Your tests automatically have access to mainnet accounts
  });
});

Best Practices

1. Use Cheatcodes for Setup

// Set up test state before each test
beforeEach(async () => {
  await connection.send("surfnet_resetNetwork", []);
  await connection.send("surfnet_setTokenAccount", [...]);
});

2. Profile Critical Paths

// Tag transactions for profiling
const result = await connection.send("surfnet_profileTransaction", [
  { transaction: tx, tag: "swap-operation" },
]);

expect(result.computeUnits).toBeLessThan(200000);

3. Use Scenarios for Edge Cases

// Test with specific mainnet conditions
await connection.send("surfnet_registerScenario", [
  { name: "low-liquidity", slots: [...] },
]);

4. Export Fixtures for CI

// Create reproducible test fixtures
const fixture = await connection.send("surfnet_exportSnapshot", [...]);

Resources

Official Links

Community

Tutorials

Skill Structure

surfpool/
├── SKILL.md                    # This file
├── resources/
│   ├── cheatcodes.md           # Complete cheatcodes reference
│   ├── cli-reference.md        # CLI commands reference
│   └── github-repos.md         # Repository links
├── examples/
│   ├── basic/
│   │   └── getting-started.ts  # Basic setup example
│   ├── cheatcodes/
│   │   └── state-manipulation.ts # Cheatcode examples
│   └── iac/
│       └── deployment.tx       # Infrastructure as Code example
├── templates/
│   ├── Surfpool.toml           # Configuration template
│   └── test-setup.ts           # Test setup template
└── docs/
    └── troubleshooting.md      # Common issues

AI Skill Finder

Ask me what skills you need

What are you building?

Tell me what you're working on and I'll find the best agent skills for you.