home / skills / dojoengine / book / dojo-review

dojo-review skill

/skills/dojo-review

This skill analyzes Dojo code for best practices, security, and performance, guiding audits of models, systems, tests, and deployment readiness.

npx playbooks add skill dojoengine/book --skill dojo-review

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
7.3 KB
---
name: dojo-review
description: Review Dojo code for best practices, common mistakes, security issues, and optimization opportunities. Use when auditing models, systems, tests, or preparing for deployment.
allowed-tools: Read, Grep, Glob
---

# Dojo Code Review

Review your Dojo code for common issues, security concerns, and optimization opportunities.

## When to Use This Skill

- "Review my Dojo code"
- "Check this system for issues"
- "Audit my models"
- "Review before deploying"

## What This Skill Does

Analyzes your code for:
- Model design patterns
- System implementation issues
- Security vulnerabilities
- Gas optimization opportunities
- Test coverage gaps
- Common mistakes

## Quick Start

**Interactive mode:**
```
"Review my Dojo project"
```

I'll ask about:
- What to review (models, systems, tests, all)
- Focus areas (security, performance)

**Direct mode:**
```
"Review the combat system for security issues"
"Check if my models follow ECS patterns"
```

## Review Categories

### Model Review

**Checks:**
- Required trait derivations (`Drop`, `Serde`)
- Key fields defined correctly (`#[key]`)
- Keys come before data fields
- Appropriate field types
- Small, focused models (ECS principle)

**Common issues:**
```cairo
// ❌ Missing required traits
#[dojo::model]
struct Position { ... }

// ✅ Required traits
#[derive(Drop, Serde)]
#[dojo::model]
struct Position { ... }

// ❌ Keys after data fields
struct Example {
    data: u32,
    #[key]
    id: u32,  // Must come first!
}

// ✅ Keys first
struct Example {
    #[key]
    id: u32,
    data: u32,
}
```

### System Review

**Checks:**
- Proper interface definition (`#[starknet::interface]`)
- Contract attribute (`#[dojo::contract]`)
- World access with namespace (`self.world(@"namespace")`)
- Input validation
- Event emissions
- Error messages

**Common issues:**
```cairo
// ❌ No input validation
fn set_health(ref self: ContractState, health: u8) {
    // Could be zero or invalid!
}

// ✅ Input validation
fn set_health(ref self: ContractState, health: u8) {
    assert(health > 0 && health <= 100, 'invalid health');
}

// ❌ No authorization check for sensitive function
fn admin_function(ref self: ContractState) {
    // Anyone can call!
}

// ✅ Authorization check
fn admin_function(ref self: ContractState) {
    let mut world = self.world_default();
    let caller = get_caller_address();
    assert(
        world.is_owner(selector_from_tag!("my_game"), caller),
        'not authorized'
    );
}
```

### Security Review

**Checks:**
- Authorization on sensitive functions
- Integer overflow/underflow
- Access control for model writes
- State consistency

**Common vulnerabilities:**
```cairo
// ❌ Integer underflow
health.current -= damage;  // Could underflow if damage > current!

// ✅ Safe subtraction
health.current = if health.current > damage {
    health.current - damage
} else {
    0
};

// ❌ Missing ownership check
fn transfer_nft(ref self: ContractState, token_id: u256, to: ContractAddress) {
    // Anyone can transfer anyone's NFT!
    let mut nft: NFT = world.read_model(token_id);
    nft.owner = to;
    world.write_model(@nft);
}

// ✅ Ownership check
fn transfer_nft(ref self: ContractState, token_id: u256, to: ContractAddress) {
    let mut world = self.world_default();
    let caller = get_caller_address();

    let mut nft: NFT = world.read_model(token_id);
    assert(nft.owner == caller, 'not owner');

    nft.owner = to;
    world.write_model(@nft);
}
```

### Gas Optimization

**Checks:**
- Minimal model reads/writes
- Efficient data types
- Unnecessary computations

**Optimization opportunities:**
```cairo
// ❌ Multiple reads of same model
let pos: Position = world.read_model(player);
let x = pos.x;
let pos2: Position = world.read_model(player);  // Duplicate!
let y = pos2.y;

// ✅ Single read
let pos: Position = world.read_model(player);
let x = pos.x;
let y = pos.y;

// ❌ Oversized types
struct Position {
    #[key]
    player: ContractAddress,
    x: u128,  // Overkill for coordinates
    y: u128,
}

// ✅ Appropriate types
struct Position {
    #[key]
    player: ContractAddress,
    x: u32,  // Sufficient for most games
    y: u32,
}
```

### Test Coverage

**Checks:**
- Unit tests for models
- Integration tests for systems
- Edge case coverage
- Failure case testing

**Coverage gaps:**
```cairo
// Missing tests:
// - Boundary values (max health, zero health)
// - Unauthorized access
// - Invalid inputs
// - Full workflow integration

// Add:
#[test]
fn test_health_bounds() { ... }

#[test]
#[should_panic(expected: ('not authorized',))]
fn test_unauthorized_access() { ... }

#[test]
fn test_spawn_move_attack_flow() { ... }
```

## Review Checklist

### Models
- [ ] All models derive `Drop` and `Serde`
- [ ] Key fields have `#[key]` attribute
- [ ] Keys come before data fields
- [ ] Models are small and focused
- [ ] Field types are appropriate size
- [ ] Custom types derive `Introspect`

### Systems
- [ ] Interface uses `#[starknet::interface]`
- [ ] Contract uses `#[dojo::contract]`
- [ ] World access uses correct namespace
- [ ] Input validation for all parameters
- [ ] Clear error messages
- [ ] Events emitted for important actions
- [ ] Caller identity verified when needed

### Security
- [ ] Sensitive functions check permissions
- [ ] Integer math handles over/underflow
- [ ] Ownership verified before transfers
- [ ] State changes are atomic

### Performance
- [ ] Minimal model reads per function
- [ ] Efficient data types used
- [ ] No unnecessary computations

### Tests
- [ ] Unit tests for models
- [ ] Integration tests for systems
- [ ] Edge cases tested
- [ ] Failure cases tested with `#[should_panic]`

## Common Anti-Patterns

### God Models
```cairo
// ❌ Everything in one model
#[dojo::model]
struct Player {
    #[key] player: ContractAddress,
    x: u32, y: u32,  // Position
    health: u8, mana: u8,  // Stats
    gold: u32, items: u8,  // Inventory
    level: u8, xp: u32,  // Progress
}

// ✅ Separate concerns (ECS pattern)
Position { player, x, y }
Stats { player, health, mana }
Inventory { player, gold, items }
Progress { player, level, xp }
```

### Missing world_default Helper
```cairo
// ❌ Repeating namespace everywhere
fn spawn(ref self: ContractState) {
    let mut world = self.world(@"my_game");
    // ...
}

fn move(ref self: ContractState, direction: u8) {
    let mut world = self.world(@"my_game");
    // ...
}

// ✅ Use internal helper
#[generate_trait]
impl InternalImpl of InternalTrait {
    fn world_default(self: @ContractState) -> dojo::world::WorldStorage {
        self.world(@"my_game")
    }
}

fn spawn(ref self: ContractState) {
    let mut world = self.world_default();
    // ...
}
```

### Not Emitting Events
```cairo
// ❌ No events
fn transfer(ref self: ContractState, to: ContractAddress, amount: u256) {
    // Transfer logic but no event
}

// ✅ Emit events for indexing
fn transfer(ref self: ContractState, to: ContractAddress, amount: u256) {
    // Transfer logic
    world.emit_event(@Transferred { from, to, amount });
}
```

## Next Steps

After code review:
1. Fix identified issues
2. Add missing tests
3. Re-run review to verify fixes
4. Use `dojo-test` skill to ensure tests pass
5. Use `dojo-deploy` skill when ready

## Related Skills

- **dojo-model**: Fix model issues
- **dojo-system**: Fix system issues
- **dojo-test**: Add missing tests
- **dojo-deploy**: Deploy after review

Overview

This skill reviews Dojo code for best practices, common mistakes, security issues, and optimization opportunities. It targets TypeScript-style Dojo projects that use Cairo/Rust patterns, and helps prepare systems, models, and tests for deployment. Use it to get actionable findings and a prioritized checklist for fixes.

How this skill works

The skill inspects models, systems, security, gas/compute patterns, and test coverage. It identifies missing trait derivations, misplaced keys, input validation gaps, authorization bugs, inefficient reads/writes, and uncovered edge cases. For each finding it explains the risk, shows corrective code patterns, and suggests tests to add. It can run interactively (ask what to review and focus areas) or directly on a named target (models, systems, tests, or all).

When to use it

  • Before a deployment to catch security, gas, and correctness issues
  • When auditing models and systems for Dojo ECS patterns
  • During code review to spot common anti-patterns (god models, missing events)
  • When adding or refactoring critical functions that change state
  • Before finalizing tests to identify coverage gaps

Best practices

  • Derive required traits (Drop, Serde) and use #[key] on key fields placed first
  • Validate all external inputs and add clear, testable error messages
  • Verify caller identity and ownership for sensitive state changes
  • Minimize model reads/writes, use appropriate small types, and avoid duplicate reads
  • Emit events for important actions and keep models focused per ECS principles

Example use cases

  • Audit a combat system for authorization holes and integer underflow risks
  • Check models to ensure keys are first, types are sized correctly, and custom types derive Introspect
  • Review system functions for correct world namespace usage and input validation
  • Identify gas optimization opportunities by consolidating model reads and downsizing types
  • Find missing unit and integration tests, and recommend #[should_panic] cases for failure paths

FAQ

What inputs do you need to run a review?

Provide the target scope (models, systems, tests, or all) and any focus areas (security, performance). Include code snippets, file paths, or a summary of systems you want audited.

Do you fix code automatically or only report issues?

I produce concrete recommendations, corrected code patterns, and test suggestions. I do not apply changes directly but can generate patch-ready snippets you can paste into your codebase.