Skip to content

Cairo Framework

This section highlights the three core components essential for building dojo apps:

  • Models: Models act like structured database entries, managing and organizing your onchain data. Like a regular ORM.

  • Systems: Implement business logic and state changes, which are just like regular Cairo contracts, but with some syntax sugar.

  • World: The central hub that connects all models and systems. The World contract ensures consistency, manages authorization, and orchestrates interactions between different parts of your application.

Mastering these will enable you to create scalable and maintainable onchain applications. If you already have a grasp of these - jump to:

Simplifying State with Dojo Models

Dojo simplifies onchain application development by providing a standardized approach to state management, similar to a regular database.

In dojo you write state like this:

#[derive(Copy, Drop, Serde)]
#[dojo::model]
pub struct Moves {
    #[key]
    pub player: ContractAddress,
    pub remaining: u8,
    pub last_direction: Direction,
    pub can_move: bool,
}

Once deployed, these models will then be reflected in your indexer, allowing you to query them in your language of choice via gRPC or GraphQL!

Breaking it down

Think of the model here as an entry into your onchain database and the #[key] is the primary key to reference it. You use this key to query your state onchain within contracts and off-chain in your clients.

#[derive(Copy, Drop, Serde)]
// This defines the model attribute. Indicating to the compile that
// this struct is a Model.
#[dojo::model]
pub struct Moves {
    // Define a Key. This acts like a primary key does in a regular ORM
    #[key]
    pub player: ContractAddress,
    // These are the fields you define for your application state
    pub remaining: u8,
    pub last_direction: Direction,
    pub can_move: bool,
}

Composing Contracts

Once you have your state designed, you want it to be effectively mutated via contracts. Dojo simplifies this by exposing an interface to interact with the world state, allowing fast queries and mutations. (It does this by extending the Cairo compiler!)

// the dojo decorator indicating to the compiler that this is a dojo
// contract.
#[dojo::contract]
mod actions {
    use starknet::{ContractAddress, get_caller_address};
 
    // import the model like a normal Cairo Struct
    use dojo_starter::models::{Moves};
 
    #[abi(embed_v0)]
    impl ActionsImpl of super::IActions<ContractState> {
 
        // You have to pass in the world as the first param,
        // which then allows you to set the models state.
        fn spawn(ref self: ContractState) {
            // Get the default world. 
            let mut world = self.world(@"dojo_starter");
 
            // Get the address of the current caller, possibly the player's address.
            let player = get_caller_address();
            // Retrieve the player's current position from the world.
            let mut position: Position = world.read_model(player);
 
            // Update the world state with the new data.
 
            // 1. Move the player's position 10 units in both the x and y direction.
            let new_position = Position {
                player, vec: Vec2 { x: position.vec.x + 10, y: position.vec.y + 10 }
            };
 
            // Write the new position to the world.
            world.write_model(@new_position);
            
            // 2. Set the player's remaining moves to 100.
            let moves = Moves { 
                player, remaining: 100, last_direction: Direction::None(()), can_move: true
            };
 
            // Write the new moves to the world.
            world.write_model(@moves);
        }
    }
}

By having it's very own Cairo compiler plugin, Dojo proposes an api that transform into comprehensive queries at compile time.

The World

Models and contracts are the foundation of your application. We encapsulate them within the World contract, which serves as a container for all models, systems, and authorization management.

overview

Takeaways

  • Models: Utilize the #[dojo::model] attribute to define structured data representing your application's state. Models function similarly to database entries, organizing and managing onchain data effectively.

  • Systems: Implement business logic and state transitions using the #[dojo::contract] attribute. Systems handle operations that modify models, ensuring seamless and reliable updates to your application's state.

  • World: Acts as the central orchestrator connecting all models and systems. The World contract maintains consistency, manages authorization, and coordinates interactions between different components, serving as the single source of truth for your application.