Skip to content

World Contract

The World contract is the beating heart of every Dojo application. Think of it as a sophisticated database and orchestrator that manages all your models, systems, and permissions while providing a unified interface for your autonomous world.

What is the World Contract?

The World contract serves as:

  • Central Database: Stores all your application's models and their data
  • Permission Manager: Controls who can write to your models
  • Event Hub: Emits events for state changes and custom events
  • Resource Registry: Manages models, systems, and contracts within namespaces
  • Upgrade Coordinator: Handles safe upgrades of your application components
// Every Dojo system gets access to the world
let mut world = self.world(@"my_namespace");
 
// Read a model
let position: Position = world.read_model(player);
 
// Write a model
world.write_model(@position);
 
// Emit an event
world.emit_event(@Moved { player, direction });

Core Concepts

Resources and Namespaces

In Dojo, everything is a resource - models, systems, events, and even the world itself. Resources are organized within namespaces to prevent conflicts and enable modular development.

// Resources are identified by their namespace and name
let world = self.world(@"my_namespace");
 
// Cairo's type system infers we want the Position model
let position: Position = world.read_model(player);

Resource Tags and Selectors

Resources in Dojo are identified by tags, which follow the namespace-resource format. Tags provide a human-readable way to reference resources in code.

// Resource tags follow the "namespace-resource" format
// Examples:
// - "dojo_starter-Position" (Position model in dojo_starter namespace)
// - "my_game-PlayerStats" (PlayerStats model in my_game namespace)
// - "dojo_starter-actions" (actions contract in dojo_starter namespace)
 
// Use tags with selector_from_tag! for permissions
world.grant_writer(selector_from_tag!("my_game-Position"), address);
world.grant_owner(selector_from_tag!("my_game-PlayerStats"), address);
Tag Structure:
  • Namespace: The logical grouping (e.g., "my_game", "dojo_starter")
  • Separator: Always a hyphen (-)
  • Resource Name: The specific resource (e.g., "Position", "PlayerStats")
Common Tag Patterns:
// Namespace tag (for namespace-level permissions)
"namespace"               // e.g., "my_game"
 
// Model tags
"namespace-ModelName"     // e.g., "my_game-Position"
 
// System tags (contract names)
"namespace-ContractName"  // e.g., "my_game-actions"
 
// Event tags
"namespace-EventName"     // e.g., "my_game-PlayerMoved"
Selectors:

Tags are converted to felt252 selectors using selector_from_tag!, which computes:

resource_selector = poseidon_hash(
    poseidon_string_hash(resource_namespace),
    poseidon_string_hash(resource_name)
)

Entity-Component-System (ECS) Architecture

The World contract implements the ECS pattern:

  • Entities: Unique identifiers (often player addresses or generated IDs)
  • Components: Your models (Position, Health, Inventory, etc.)
  • Systems: Functions that operate on components
// Entity: player address
let player = get_caller_address();
 
// Components: models attached to the entity
let position: Position = world.read_model(player);
let health: Health = world.read_model(player);
 
// Systems: functions that modify components
world.write_model(@updated_position);

Permissions and Security

The World contract implements a resource-based permission system with two permission types:

  • Owner: Can manage resources, grant permissions, and upgrade resources
  • Writer: Can write data into resource storage

Resource Hierarchy (order of precedence):

  1. World → Can access all resources
  2. Namespace → Can access all resources in that namespace
  3. Model/Contract/Event → Can access the specific resource

Key Points:

  • Reading is always permissionless
  • Writing requires Writer permission on the resource or its namespace
  • When you deploy to a world, you automatically become owner of your namespace

Getting Started

Basic World Access

Every Dojo system gets access to the world through the namespace-specific world() function:

#[dojo::contract]
mod actions {
    use super::IActions;
    use dojo::model::ModelStorage;
    use dojo::world::WorldStorage;
 
    #[abi(embed_v0)]
    impl ActionsImpl of IActions<ContractState> {
        fn spawn(ref self: ContractState) {
            // Access world with your namespace
            let mut world = self.world(@"my_game");
 
            // Now you can read models (always allowed) and write models (requires permission)
            let player = get_caller_address();
            let position = Position {
                player,
                vec: Vec2 { x: 0, y: 0 }
            };
 
            world.write_model(@position);
        }
    }
}

Common Usage Patterns

Reading Models:

// Single key
let position: Position = world.read_model(player);
 
// Multiple keys
let resource: GameResource = world.read_model((player, location));

Writing Models:

let mut position: Position = world.read_model(player);
position.vec.x += 1;
world.write_model(@position);

Emitting Events:

#[derive(Copy, Drop, Serde)]
#[dojo::event]
pub struct Moved {
    #[key]
    pub player: ContractAddress,
    pub direction: Direction,
}
 
world.emit_event(@Moved { player, direction });

Key Benefits

Unified Interface

Instead of managing multiple contracts and their interactions, you have one consistent interface:

// All operations go through the world
world.read_model(keys);
world.write_model(@model);
world.emit_event(@event);

Automatic Indexing

The World contract automatically emits events for all state changes, enabling automatic indexing by Torii for your frontend applications.

Upgradeable Architecture

Components can be upgraded safely without breaking existing functionality or losing data:

# Make changes to your models or systems, then build
sozo build
 
# Run migrate - automatically detects and upgrades changed resources
sozo migrate

The sozo migrate command automatically detects which resources have changed and calls the appropriate upgrade functions on the world contract.

Gas Optimization

The World contract includes several optimizations:

  • Batch Operations: Write multiple models in one transaction
  • Efficient Storage: Optimized storage layouts for different data types
  • Permission Caching: Hierarchical permission checks reduce gas costs

The World Interface

The World contract exposes a complete interface for external interactions. While you typically use the high-level API in your systems, understanding the full interface helps with advanced use cases:

// Generate unique IDs
let new_id = world.uuid();
 
// Check permissions
let can_write = world.is_writer(resource_selector, address);
 
// Manage permissions
world.grant_writer(resource_selector, address);

Next Steps

Now that you understand the World contract's role, dive deeper into specific areas:

  • API Reference - Complete API documentation with examples
  • Permissions - Understanding and managing permissions
  • Events - Working with the event system
  • Metadata - Configuring world and resource metadata

Integration with Other Components

The World contract integrates seamlessly with other Dojo components:

  • Models - Define your data structures
  • Systems - Implement your game logic
  • Sozo - Deploy and manage your world
  • Torii - Index and query your world's data

The World contract is your application's foundation - everything else builds on top of it.