dojo-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Dojo Code Review

Dojo代码审查

Review your Dojo code for common issues, security concerns, and optimization opportunities.
审查你的Dojo代码,找出常见问题、安全隐患以及优化空间。

When to Use This Skill

何时使用该Skill

  • "Review my Dojo code"
  • "Check this system for issues"
  • "Audit my models"
  • "Review before deploying"
  • “审查我的Dojo代码”
  • “检查这个系统的问题”
  • “审计我的模型”
  • “部署前审查”

What This Skill Does

该Skill的功能

Analyzes your code for:
  • Model design patterns
  • System implementation issues
  • Security vulnerabilities
  • Gas optimization opportunities
  • Test coverage gaps
  • Common mistakes
分析你的代码,涵盖以下方面:
  • 模型设计模式
  • 系统实现问题
  • 安全漏洞
  • Gas优化空间
  • 测试覆盖缺口
  • 常见错误

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 my Dojo project"
我会询问以下内容:
  • 审查范围(模型、系统、测试、全部)
  • 重点关注领域(安全、性能)
直接模式:
"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,
}
检查项:
  • 必需的trait派生(
    Drop
    Serde
  • 正确定义关键字段(
    #[key]
    属性)
  • 关键字段位于数据字段之前
  • 合适的字段类型
  • 小巧且职责单一的模型(ECS原则)
常见问题:
cairo
// ❌ 缺少必需的trait
#[dojo::model]
struct Position { ... }

// ✅ 包含必需的trait
#[derive(Drop, Serde)]
#[dojo::model]
struct Position { ... }

// ❌ 关键字段在数据字段之后
struct Example {
    data: u32,
    #[key]
    id: u32,  // 必须放在前面!
}

// ✅ 关键字段在前
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'
    );
}
检查项:
  • 正确定义接口(
    #[starknet::interface]
  • 合约使用
    #[dojo::contract]
    属性
  • 使用命名空间访问World(
    self.world(@"namespace")
  • 输入验证
  • 事件触发
  • 错误信息
常见问题:
cairo
// ❌ 无输入验证
fn set_health(ref self: ContractState, health: u8) {
    // 可能为零或无效值!
}

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

// ❌ 敏感函数未做权限检查
fn admin_function(ref self: ContractState) {
    // 任何人都可以调用!
}

// ✅ 权限检查
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);
}
检查项:
  • 敏感函数的权限检查
  • 整数溢出/下溢处理
  • 模型写入的访问控制
  • 状态一致性
常见漏洞:
cairo
// ❌ 整数下溢
health.current -= damage;  // 如果damage大于current,会发生下溢!

// ✅ 安全减法
health.current = if health.current > damage {
    health.current - damage
} else {
    0
};

// ❌ 缺少所有权检查
fn transfer_nft(ref self: ContractState, token_id: u256, to: ContractAddress) {
    // 任何人都可以转移他人的NFT!
    let mut nft: NFT = world.read_model(token_id);
    nft.owner = to;
    world.write_model(@nft);
}

// ✅ 所有权检查
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

Gas优化

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,
}
检查项:
  • 最小化模型读写次数
  • 高效的数据类型
  • 避免不必要的计算
优化机会:
cairo
// ❌ 多次读取同一模型
let pos: Position = world.read_model(player);
let x = pos.x;
let pos2: Position = world.read_model(player);  // 重复读取!
let y = pos2.y;

// ✅ 单次读取
let pos: Position = world.read_model(player);
let x = pos.x;
let y = pos.y;

// ❌ 类型过大
struct Position {
    #[key]
    player: ContractAddress,
    x: u128,  // 坐标用这个类型太冗余
    y: u128,
}

// ✅ 合适的类型
struct Position {
    #[key]
    player: ContractAddress,
    x: u32,  // 对大多数游戏来说足够用
    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() { ... }
检查项:
  • 模型的单元测试
  • 系统的集成测试
  • 边界场景覆盖
  • 失败场景测试
覆盖缺口:
cairo
// 缺失的测试:
// - 边界值(最大生命值、零生命值)
// - 未授权访问
// - 无效输入
// - 完整工作流集成

// 补充:
#[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
  • 所有模型都派生
    Drop
    Serde
  • 关键字段带有
    #[key]
    属性
  • 关键字段位于数据字段之前
  • 模型小巧且职责单一
  • 字段类型大小合适
  • 自定义类型派生
    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
  • 接口使用
    #[starknet::interface]
  • 合约使用
    #[dojo::contract]
  • 访问World使用正确的命名空间
  • 所有参数都有输入验证
  • 错误信息清晰
  • 重要操作触发事件
  • 必要时验证调用者身份

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]
  • 模型有单元测试
  • 系统有集成测试
  • 测试边界场景
  • 使用
    #[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 }
cairo
// ❌ 所有内容放在一个模型里
#[dojo::model]
struct Player {
    #[key] player: ContractAddress,
    x: u32, y: u32,  // 位置
    health: u8, mana: u8,  // 属性
    gold: u32, items: u8,  // 背包
    level: u8, xp: u32,  // 进度
}

// ✅ 分离关注点(ECS模式)
Position { player, x, y }
Stats { player, health, mana }
Inventory { player, gold, items }
Progress { player, level, xp }

Missing world_default Helper

缺少world_default助手函数

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();
    // ...
}
cairo
// ❌ 重复使用命名空间
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");
    // ...
}

// ✅ 使用内部助手函数
#[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 });
}
cairo
// ❌ 无事件触发
fn transfer(ref self: ContractState, to: ContractAddress, amount: u256) {
    // 转移逻辑但无事件
}

// ✅ 触发事件用于索引
fn transfer(ref self: ContractState, to: ContractAddress, amount: u256) {
    // 转移逻辑
    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
代码审查完成后:
  1. 修复发现的问题
  2. 补充缺失的测试
  3. 重新运行审查以验证修复效果
  4. 使用
    dojo-test
    Skill确保测试通过
  5. 准备就绪后使用
    dojo-deploy
    Skill

Related Skills

相关Skills

  • dojo-model: Fix model issues
  • dojo-system: Fix system issues
  • dojo-test: Add missing tests
  • dojo-deploy: Deploy after review
  • dojo-model: 修复模型问题
  • dojo-system: 修复系统问题
  • dojo-test: 补充缺失的测试
  • dojo-deploy: 审查后部署