Create Dojo models for storing game state with proper key definitions, trait derivations, and ECS patterns. Use when defining game entities, components, or state structures.
78
Does it follow best practices?
If you maintain this skill, you can automatically optimize it using the tessl CLI to improve its score:
npx tessl skill review --optimize ./path/to/skillValidation for skill structure
Create Dojo models that define your game's state using Entity Component System (ECS) patterns.
Generates Cairo model structs with:
#[dojo::model] attributeDrop, Serde)#[key])Interactive mode:
"Add a model for player positions"I'll ask about:
Direct mode:
"Create a Position model with player as key and x, y coordinates"In your model file (e.g., models.cairo):
use starknet::ContractAddress;
// For nested structs that aren't models
use dojo::meta::Introspect;In systems that use models:
// Import your models
use my_project::models::{Player, Position, Inventory};
// Import Dojo storage traits
use dojo::model::{ModelStorage, ModelValueStorage};Reading/Writing models in a system:
// Get world storage
let mut world = self.world_default();
// Read - provide all #[key] values
let player: Player = world.read_model(player_address);
// Write - model must contain all keys and data
world.write_model(@player);Models are Cairo structs annotated with #[dojo::model].
They act as a key-value store where #[key] fields define the lookup key.
#[derive(Drop, Serde)]
#[dojo::model]
struct Moves {
#[key]
player: ContractAddress,
remaining: u8,
}Required traits:
Drop - Cairo ownership systemSerde - Serialization for on-chain storageOptional traits:
Copy - Add when you need to copy values (for primitive types)Models keyed by player address:
#[derive(Drop, Serde)]
#[dojo::model]
struct Position {
#[key]
player: ContractAddress,
vec: Vec2,
}
#[derive(Drop, Copy, Serde, Introspect)]
struct Vec2 {
x: u32,
y: u32,
}Custom nested structs must derive Introspect for Dojo to understand their structure.
Multiple keys for relationships (all keys must be provided when reading):
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct GameResource {
#[key]
player: ContractAddress,
#[key]
location: ContractAddress,
balance: u8,
}Read with tuple of all keys:
let resource: GameResource = world.read_model((player, location));Constant key for global settings:
const RESPAWN_DELAY: u128 = 9999999999999;
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct GameSetting {
#[key]
setting_id: u128,
setting_value: felt252,
}
// Usage
world.write_model(@GameSetting {
setting_id: RESPAWN_DELAY,
setting_value: (10 * 60).into()
});Small, focused models that can be combined on entities:
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Position {
#[key]
id: u32,
x: u32,
y: u32,
}
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Health {
#[key]
id: u32,
health: u8,
}
// Human has Position + Health + Potions
// Orc has Position + Health (no Potions)#[key] fieldGet the world storage in your system:
use dojo::model::{ModelStorage, ModelValueStorage};
let mut world = self.world(@"my_namespace");world.write_model(@Position { player, vec: Vec2 { x: 0, y: 0 } });let position: Position = world.read_model(player);let resource: GameResource = world.read_model((player, location));let entity_id = world.uuid();
world.write_model(@Health { id: entity_id, health: 100 });u8, u16, u32, u64, u128, u256 - Unsigned integersfelt252 - Field elementsbool - BooleansContractAddress - Starknet addressesIntrospectIntrospectAfter creating models:
dojo-system skill to create systems that use your modelsdojo-test skill to test model read/write operationsdojo-config skill to configure permissions3c1874f
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.