How it works
You run one command.
A PTB that touches real mainnet pools just works, locally. No manual object dumping.
This page shows how. We follow one transaction from start to finish.
The big idea: copy-on-read
Galene is built on one trick. A copy-on-read fork.
Mainnet is the source of truth. It is read-only. Every write you make stays local.
Three things follow.
It starts instantly. No chain download. The fork boots empty. It stays empty until something asks for an object.
It fetches on demand. Touch an object that is not local yet. Galene pulls it from a mainnet fullnode and caches it. The next read is local.
Your writes stay home. Galene never writes to mainnet. Your mutations, mints, and overwrites land in the local store. Break whatever you want. The real chain is safe.
Mainnet is the default fork source, not the only one. Point Galene at any Sui network, mainnet,
testnet, devnet, localnet, or a private fullnode, by setting rpc in the [fork] config. Everywhere this page says “mainnet,” read “your fork source.”
The four parts
| Part | Job | Source |
|---|---|---|
| Oceanus | Fetches a missing object from a mainnet fullnode. Decodes its contents, owner, and type. | fetch.rs |
| Poseidon | Wraps Sui’s in-memory chain. Injects forked objects. Runs transactions. | lib.rs |
| Plouto | Fabricates a Coin<T> directly. Any type, any balance, any owner. | coin.rs |
| Proteus | Turns a transaction into a per-command object graph. | graph.rs |
One transaction, end to end
This is the loop that makes mainnet DeFi work locally.
It lives in the galene_executeTransactionBlock handler in server.rs.
You submit a transaction
You send serialized TransactionData. These are the exact bytes the TS SDK builds before signing.
No signature needed. Galene runs it as an impersonated sender.
Poseidon finds what is missing
Galene reads the transaction’s input objects and gas payment. It asks the local store which ones are not present yet. That list is the work to do.
Oceanus fetches each one
For each missing object, Galene calls sui_getObject on a mainnet fullnode. It decodes the contents, owner, and type.
The objects enter the VM
Galene rebuilds each object and writes it into the local chain. It uses the same path genesis uses. The VM cannot tell it apart from a native object.
Poseidon runs it
Every object is now local. The real Move VM executes the transaction. No ownership or signature checks get in your way.
You get the results
You get the digest, the result, the gas used, and the created and mutated counts. You also get the list of objects Galene forked to make the run work.
Version-aware by design
Sui gives every object an id and a version. Galene keeps both.
The store keys each object by id and version. Not by id alone.
A fetched object arrives at its real mainnet version. A local change writes a new version. It does not overwrite the old one. Reads return the latest.
Every fetch pulls the object’s current upstream version, so a fork tracks the source’s latest state.
Galene also tags each object by origin. Mainnet, or local. It always knows which is which.
// keyed by id AND version
ObjectKey { id, version }
// a local change supersedes, never clobbers
fn next_version(store, id) -> u64 {
store.get_latest(id).map(|o| o.version + 1).unwrap_or(1)
}
// every object carries its origin: Mainnet | LocalThis versioning is what powers the object-graph debugger.
Real execution, not simulation
Poseidon wraps Sui’s own engine. It is the real Move VM, running its own protocol version.
So gas, type checks, and effects are all real. If a transaction passes here, it passes on mainnet. The only difference is that you choose the starting state.
Decoupled protocol version. sui_getProtocolConfig reports the engine’s own protocol version, not mainnet’s. So the fork does not break when mainnet bumps its protocol, and clients never choke on a version mismatch.
Genesis-path injection. A forked object is rebuilt from its mainnet bytes. It is written through the exact path genesis uses. No special cases.
Direct coin fabrication. Plouto mints a Coin<T> by building the object directly. No TreasuryCap needed in a local fork.
Replay and the object graph
Galene also works backward. Give it any past mainnet digest.
Replay collects the artifacts. galene replay <digest> runs Sui’s own replay with tracing. It writes the results into .galene/replays/<digest>/. Galene parses them: the objects touched, the full effects, the gas report, and a compressed Move trace.
Proteus builds the graph. It reads the PTB and the effects. Then it builds a per-command graph: the input objects, what each command reads, and what the transaction created, mutated, or deleted. Studio renders it as a graph you can scrub.
The server
Galene serves JSON-RPC on 127.0.0.1:9123.
Standard Sui methods keep the SDK and CLI happy. The galene_* cheatcodes do the rest.
The same fork is also reachable over the modern Sui gRPC interface (sui.rpc.v2), served from 127.0.0.1:9000. Both servers share one fork state. See gRPC.
Every call changes one shared fork state behind a lock. Every call is also broadcast on a live channel. That is how the terminal dashboard and Galene Studio stay in sync as you work.
ForkState {
store, // version-aware objects
engine, // the Move VM (Poseidon)
epoch, clock_ms, // time travel
active_sender, // impersonation
package_overrides, // hot-swapped Move
snapshots, // snapshot / revert
}Keep reading
- Cheatcodes. The full
galene_*surface. - Historical replay. The replay and graph commands.