How Dubhe Works
For a new developer, Dubhe can be understood as a three-layer division of responsibility. You are only responsible for the top layer — the framework handles everything else.
Layer 1 — Your config (TypeScript)
Everything starts with dubhe.config.ts. You only need to declare what data your contract has:
export const dubheConfig = defineConfig({
name: 'my_game',
resources: {
level: 'u32', // each player has a level
stats: {
// each player has attack / hp
fields: { attack: 'u32', hp: 'u32' }
}
}
});You do not need to think about how this data is stored on-chain, how it is serialized, or how events are emitted — none of that is your responsibility.
Layer 2 — Auto-generated contract code (codegen)
After running dubhe schemagen, the framework generates Move modules from your config:
sources/codegen/resources/level.move ← auto-generated, do not edit
sources/codegen/resources/stats.move ← auto-generated, do not editThese modules provide a complete, type-safe API:
level::set(dh, player, 10, ctx); // write
level::get(dh, player); // read
level::has(dh, player); // check existenceKey insight: You never need to write or fully understand this layer. It is just a bridge between your config and the Framework.
Layer 3 — Your business logic (systems)
The only code you actually write lives in sources/systems/ — your game rules or application logic:
// sources/systems/combat_system.move ← you write this
public entry fun level_up(dh: &mut DappHub, ctx: &mut TxContext) {
let player = address_system::ensure_origin(ctx);
let lv = level::get(dh, player);
level::set(dh, player, lv + 1, ctx); // calls the generated API
}The foundation — Dubhe Framework (already deployed, nothing to set up)
The generated code ultimately calls Dubhe Framework, a package already deployed on Sui. It handles:
| What you trigger | What the Framework does automatically |
|---|---|
level::set(...) | Writes to DappHub shared object via dynamic fields |
| Any write operation | Emits a SetRecord event (subscribable by frontends / indexers) |
| First contract deployment | Registers your DApp in DappHub, allocates storage credits |
| Contract upgrade | Validates version, blocks old package versions from writing |
All DApps share a single DappHub object but are fully isolated through DappKey — a type-level identity — so different DApps’ data never interfere with each other.
The full flow at a glance
dubhe.config.ts (you write this)
│
▼ dubhe schemagen
codegen/ Move modules (auto-generated)
│
▼ you call from systems/
Your business logic (you write this)
│
▼ runtime calls
Dubhe Framework (on-chain, already deployed)
│
▼ automatically fires
SetRecord events → SDK syncs state to your frontendThree things every new developer should know
1. You only touch two places.
dubhe.config.ts for data declarations and sources/systems/ for business logic. Everything else is either auto-generated or already lives on-chain.
2. schemagen is safe to re-run.
Every time you change the config, run dubhe schemagen again. The codegen/ directory is fully regenerated, but your hand-written systems/ files and deploy_hook.move are never touched.
3. Frontend sync is zero-config.
Every on-chain state change automatically fires an event. Pair it with @0xobelisk/sui-sdk subscriptions and your frontend stays in sync without polling or any extra sync code.