mcpField
S
surfpool

surfpool

github.com/sendaifun开发130220 次安装13 周前更新
代码仓库

关于 surfpool

surfpool 是面向 Solana 开发者的本地开发环境,可替代 `solana-test-validator`,并按需读取 Mainnet 数据。它支持状态操控、部署自动化和交易分析,适用于基于真实链上账户进行测试。

主要功能

  1. 01按需分叉 Mainnet 账户和程序
  2. 02通过 RPC Cheatcodes 操控状态与时间
  3. 03自动检测 Anchor 项目并部署程序
  4. 04用 Surfpool Studio 检查交易和计算用量
  5. 05通过 txtx DSL 编写可复现部署

适用场景

  1. 01使用 Mainnet 账户状态测试 Solana 程序
  2. 02设置余额、账户数据或网络时间来覆盖边界情况
  3. 03分析交易计算用量并查看账户状态
  4. 04为测试和 CI 导出可复现的交易快照

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sendaifun/skills --skill surfpool

安装后在 Claude Code 中输入/surfpool确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

docs/
troubleshooting.md
examples/basic/
getting-started.ts
examples/cheatcodes/
state-manipulation.ts
examples/iac/
deployment.tx
resources/
cheatcodes.md
cli-reference.md
github-repos.md
templates/
Surfpool.toml
test-setup.ts

详细介绍

Surfpool - Solana 开发环境

Surfpool 权威指南——开发者 Solana 之旅的起点。它可直接替代 solana-test-validator,利用即时获取的 Mainnet 账户在本地模拟程序。

什么是 Surfpool?

Surfpool 是一个全面的开发环境,将本地优先测试与真实 Mainnet 数据访问相结合:

  • Mainnet 分叉 - 即时克隆 Mainnet 上的账户、程序和代币余额
  • 作弊码 - 用于时间穿越、余额调整和状态控制的特殊 RPC 方法
  • 基础设施即代码 - 使用 txtx DSL 实现可复现、可审计的部署
  • Surfpool Studio - 内嵌仪表板,支持交易检查和性能分析
  • 通用水龙头 - 通过单一界面获取 SOL、USDC、USDT、BONK
主要优势
功能说明
即时启动无需 2 TB 快照,可在 Raspberry Pi 上运行
惰性分叉按需复制数据的策略会在需要时拉取 mainnet 数据
完全兼容适用于 solana-cli、Anchor、钱包和区块链浏览器
零配置自动检测 Anchor 项目并部署程序
统计数据
  • 460+ GitHub 星标
  • 100+ 个复刻
  • Apache 2.0 许可证
  • 当前版本:v1.0.0

安装

自动安装程序(推荐)
curl -sL https://run.surfpool.run/ | bash
Homebrew(macOS)
brew install txtx/taps/surfpool
从源代码安装
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

快速开始

启动本地网络
# 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
访问地址
服务URL说明
RPC 端点http://127.0.0.1:8899标准 Solana RPC
WebSocketws://127.0.0.1:8900实时订阅
Surfpool Studiohttp://127.0.0.1:18488Web 仪表板

CLI 命令

surfpool start

启动本地 Surfnet 网络。

surfpool start [OPTIONS]

选项:

选项默认值说明
-m, --manifest-file-path./Surfpool.toml清单文件路径
-p, --port8899RPC 端口
-o, --host127.0.0.1主机地址
-s, --slot-time400Slot 时间,单位为毫秒
-u, --rpc-urlhttps://api.mainnet-beta.solana.com源 RPC URL
--no-tui-禁用终端 UI
--debug-启用调试日志
--no-deploy-禁用自动部署
-r, --runbookdeployment要执行的运行手册
-a, --airdrop-要空投的公钥
-q, --airdrop-amount10000000000000空投数量(lamports)
-k, --airdrop-keypair-path-空投使用的密钥对路径
--no-explorer-禁用区块链浏览器
使用示例
# 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 配置

在项目根目录中创建 Surfpool.toml:

[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

作弊码

Surfpool 提供特殊 RPC 方法,用于测试期间进行高级状态操作。

账户操作
surfnet_setAccount

设置任意账户数据:

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

创建或修改代币账户:

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

从 mainnet 克隆程序:

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

将账户重置为 mainnet 状态:

await connection.send("surfnet_resetAccount", [
  {
    pubkey: "AccountPubkey...",
    includeOwnedAccounts: true,
  },
]);
时间控制
surfnet_timeTravel

推进网络时间:

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

控制区块生成:

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

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

逐步推进时钟:

await connection.send("surfnet_advanceClock", [
  { slots: 100 },
]);
交易性能分析
surfnet_profileTransaction

分析交易执行情况:

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

按标签获取性能分析结果:

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

将整个网络重置为初始状态:

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

获取当前网络时间:

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

Surfpool Studio

访问 http://127.0.0.1:18488 上的 Web 控制面板,使用以下功能:

  • 交易检查器 - 查看交易详情和字节级差异
  • 账户浏览器 - 探索账户状态和历史记录
  • 计算分析器 - 分析每条指令的计算单元用量
  • 通用水龙头 - 申请 SOL 和代币
  • 网络状态 - 监控插槽、纪元和区块生成情况

基础设施即代码

Surfpool 集成了 txtx DSL,可用于可复现的部署。

Runbook 结构
# 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]
}
运行 Runbook
# Run specific runbook
surfpool start -r deployment

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

场景和固定数据

场景

定义用于测试的账户状态序列:

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

导出交易固定数据,以便进行可复现的测试:

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));

与 Anchor 集成

Surfpool 会自动检测 Anchor 项目并处理部署:

# In an Anchor project directory
surfpool start
# Programs in target/deploy/ are automatically deployed
使用 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
  });
});

最佳实践

1. 使用 Cheatcodes 进行设置
// Set up test state before each test
beforeEach(async () => {
  await connection.send("surfnet_resetNetwork", []);
  await connection.send("surfnet_setTokenAccount", [...]);
});
2. 对关键路径进行性能分析
// Tag transactions for profiling
const result = await connection.send("surfnet_profileTransaction", [
  { transaction: tx, tag: "swap-operation" },
]);

expect(result.computeUnits).toBeLessThan(200000);
3. 使用场景测试边界情况
// Test with specific mainnet conditions
await connection.send("surfnet_registerScenario", [
  { name: "low-liquidity", slots: [...] },
]);
4. 为 CI 导出固定数据
// Create reproducible test fixtures
const fixture = await connection.send("surfnet_exportSnapshot", [...]);

资源

官方链接
社区
教程

技能结构

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

评论

支持 Markdown