Surfpool 1.0
Today we're putting a 1.0 on Surfpool. Mainnet forking, cheatcodes, a new TUI, new docs, and Studio. A complete toolkit for Solana development.

Today we're putting a 1.0 on Surfpool.
For the occasion, we're doing a launch week โ four days, four features, starting today.
| Day | Feature |
|---|---|
| Today | Mainnet Forking & Cheatcodes |
| Day 2 | Infrastructure as Code |
| Day 3 | Studio |
| Day 4 | ๐ |
We met with a lot of builders at Breakpoint this year. Conversation after conversation, it became clear: Surfpool can already help with so many workflows, but that potential is sitting dormant. People don't know what's possible.
This week, we fix that. Each day, we'll focus on one feature in depth.
Today: Mainnet Forking & Cheatcodes โ the core of what makes Surfpool different.
The Pain
Every Solana developer knows this loop:
- You're testing locally
- Transaction fails โ account doesn't exist
- You clone the account manually
- Another account is missing
- Clone that too
- Eventually your local state drifts from mainnet
- You're debugging phantom issues that don't exist in production
This is the reality of solana-test-validator. It works, but it's friction. Every missing account is a context switch. Every stale clone is a potential bug.
The Fix
Surfpool fetches accounts just-in-time from Mainnet as your transactions need them.
$curl -sL https://run.surfpool.run | bash$surfpool
That's it. No manual cloning. No stale state. Your transactions get exactly what they need, when they need it.
How It Works
When you send a transaction to Surfpool, it analyzes which accounts are needed. If an account doesn't exist locally, Surfpool fetches it from Mainnet before executing.
This means you can:
- Test against real program state โ no need to clone programs manually
- Interact with any token โ actual mint metadata, actual decimals
- Simulate swaps โ real liquidity pool data, real prices
- Debug production issues โ replay transactions locally with real state
The accounts are cached locally, so subsequent transactions are fast. But you always start with real, current mainnet data.
Under the Hood
This sounds simple, but getting it right required hundreds of iterations based on feedback from developers in the field. Here are just a few examples among all the nuances we added to the mainnet fetching logic.
Program Accounts
When you fetch a program, you don't just need the program account โ you need its program data account too. Surfpool detects executable accounts and automatically fetches the associated data account in the same request. No extra round-trips, no missing bytecode.
Program account (executable=true)
โโโ Program data account (fetched automatically)
Token Accounts & ATAs
Token accounts reference a mint. If we fetch the token account but not the mint, your transaction fails with a cryptic error. Surfpool unpacks every token account, extracts the mint address, and fetches it alongside.
This works for both SPL Token and Token-2022. We detect the program owner and handle both interfaces transparently.
Token account
โโโ Mint account (fetched automatically)
The Closed Account Problem
Here's a subtle one: what happens when you close an account locally, then send another transaction that references it?
Without special handling, Surfpool would see "account missing locally" and fetch it from mainnet โ undoing your close. We track closed accounts explicitly. Once closed, an account stays closed, even if it still exists on mainnet.
Local-First Resolution
The resolution order matters:
- Check local SVM cache
- If missing, fetch from mainnet
- Cache the result
- Local modifications always win
This means you can fork mainnet state, modify it locally, and your modifications persist โ even if the mainnet state changes. Your local environment is deterministic.
The Compromises
We made deliberate tradeoffs:
-
Mint fetching is sequential, not batched. When fetching multiple token accounts, we query each mint individually. Batching would be faster, but the complexity wasn't worth it. For most workflows, you're not fetching hundreds of token accounts at once.
-
Program data addresses are derived, not queried. We compute the program data address deterministically rather than looking it up. This is faster and works for all standard programs.
These compromises came from real-world usage. We iterated with hundreds of developers to find the right balance between correctness, performance, and simplicity.
Cheatcodes
Mainnet data is the foundation, but real-world testing requires more. You need to manipulate state, control time, and simulate edge cases.
That's where cheatcodes come in โ special RPC methods that give you superpowers. Here are the essentials. (Full documentation)
surfnet_setTokenAccount
Set any token balance for any wallet, instantly.
$curl -X POST http://localhost:8899 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id": 1,"method": "surfnet_setTokenAccount","params": ["ABAq2R9gSpDDGguP9D45rvchVzeRSfkHRJ1zQVMGwE8M","EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",{ "amount": 1000000000 }]}'
This powers the Universal Faucet in Studio โ get any token you need in one click. More on that when we cover Studio on Day 3.
surfnet_timeTravel
Advance the clock to test time-dependent logic.
$curl -X POST http://localhost:8899 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id": 1,"method": "surfnet_timeTravel","params": [{"absoluteSlot": 1000000000}]}'
Lockup periods, vesting schedules, time-weighted oracles โ test them all without waiting.
surfnet_streamAccount
Mark an account to always fetch fresh from mainnet.
$curl -X POST http://localhost:8899 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id": 1,"method": "surfnet_streamAccount","params": ["ABAq2R9gSpDDGguP9D45rvchVzeRSfkHRJ1zQVMGwE8M"]}'
By default, Surfpool caches fetched accounts. But sometimes you need fresh data โ price oracles, pool state, anything that changes frequently on mainnet. streamAccount adds the account to a "no-cache" list: every time a transaction touches it, Surfpool fetches the latest state from mainnet instead of using the cached version.
surfnet_exportSnapshot
Export your current state as a fixture.
$curl -X POST http://localhost:8899 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id": 1,"method": "surfnet_exportSnapshot","params": [{"scope": "network"}]}'
Here's the powerful part: these snapshots are pre-execution state. They can be loaded directly into LiteSVM for deterministic unit tests. Set up complex state in Surfpool, export it, and replay in CI โ no network calls, instant execution. We'll show this workflow in detail when we cover Studio on Day 3.
Get Started
# Install$curl -sL https://run.surfpool.run | bash# Start$surfpool
Your existing Solana tooling works out of the box โ point your RPC to http://localhost:8899.
Open localhost:18488 to see the Studio dashboard.
That's Day 1. Tomorrow, we go deep on Infrastructure as Code โ version-controlled deployments for Solana.
Final Thoughts
It took a village to ship this v1. Huge thanks to all the contributors who helped make Surfpool what it is today:
0xsouravm, ADPtheGreat, Allen George, Anthony Anderson, armariya, Arthur Bretas, Aursen, AvhiMaz, byte, deepanshu, dlock, Dodecahedr0x, dvansari65, JayaKrishna, Nirav Joshi, nk_ysg, Ognyan Chikov, Ozodimgba, pawan, serejke, Shivendra Mishra, Shradesh Jodawat, shubham, Sonic, spacemandev, subh, tanmay4l, The Wuh, Valentin Madrid, Victor Carvalho, wu aoxiang, xiaodao, and Zeeshanali.
Learn More
We've been covering Surfpool in depth on our YouTube channel โ tutorials, deep dives, and live coding sessions. Check out the Surfpool playlist.
Links:
- Documentation โ Getting started, API reference, cheatcodes
- GitHub โ Star the repo if Surfpool helps your workflow โญ
- Discord โ Join the community
- X / Twitter โ Follow for updates