Skip to main content

๐Ÿ“œ Move Contract Development

Master the art of building scalable, secure Move smart contracts with Dubheโ€™s Entity Component System

Prerequisites: Basic understanding of blockchain concepts and programming. Complete the blockchain basics guide if youโ€™re new to blockchain development.

๐ŸŽฏ What Youโ€™ll Learn

ECS Patterns

Build contracts using Entity Component System architecture

Move Best Practices

Write secure, gas-efficient Move code

Schema-Driven Development

Generate contracts automatically from schema definitions

๐Ÿ“‹ Development Overview

Dubhe transforms traditional smart contract development by introducing Entity Component System (ECS) architecture to blockchain applications. Instead of monolithic contracts, you build modular, composable systems.
// Monolithic player contract - hard to extend
module game::player {
    struct Player has key, store {
        id: UID,
        name: String,
        health: u64,
        level: u8,
        inventory: vector<Item>,
        position_x: u64,
        position_y: u64,
        experience: u64,
        // Adding new features requires modifying this struct
    }
    
    // All logic mixed together
    public fun create_player(...) { /* complex logic */ }
    public fun level_up(...) { /* more complex logic */ }
    public fun move_player(...) { /* even more logic */ }
    // Hard to test individual features
}

๐Ÿ—๏ธ Development Workflow

1

Design Your Game Schema

Start with data structure design - this drives everything else.
# schemas/game.yaml
World: 
  components:
    HealthComponent:
      current: u64
      maximum: u64
    
    PositionComponent:
      x: u64  
      y: u64
    
    PlayerComponent:
      name: string
      owner: address
    
    InventoryComponent:
      items: "vector<u64>"
      capacity: u32
  
  systems:
    - CombatSystem
    - MovementSystem  
    - InventorySystem
Schema-First Development: Your schema definition automatically generates Move contracts, TypeScript types, and client SDK methods. This ensures type safety from blockchain to frontend.
2

Generate Base Contracts

# Generate Move contracts from schema
dubhe codegen

# Files created:
# - sources/codegen/world.move
# - sources/codegen/components/
# - sources/codegen/systems/
// Auto-generated world.move
module game::world {
    use sui::object::{Self, UID};
    use sui::table::{Self, Table};
    use sui::bag::{Self, Bag};
    
    struct World has key {
        id: UID,
        entities: Table<u64, EntityData>,
        next_entity_id: u64,
    }
    
    struct EntityData has store {
        components: Bag,
    }
    
    // Auto-generated helper functions
    public fun spawn_entity(world: &mut World): u64 { ... }
    public fun add_component<T: store>(world: &mut World, entity: u64, component: T) { ... }
    public fun get_component<T: store>(world: &World, entity: u64): &T { ... }
    // ... more helper functions
}
3

Implement Game Logic

Focus on the unique parts - Dubhe handles the boilerplate.
module game::combat_system {
    use game::world::{Self, World};
    use game::components::{HealthComponent, PlayerComponent};
    
    const EDamageExceedsHealth: u64 = 1;
    const ENotPlayerOwner: u64 = 2;
    
    public entry fun deal_damage(
        world: &mut World,
        target_entity: u64,
        damage_amount: u64,
        ctx: &TxContext
    ) {
        // Validate target exists
        assert!(world::has_component<HealthComponent>(world, target_entity), 0);
        
        // Apply damage
        let health = world::get_mut_component<HealthComponent>(world, target_entity);
        
        if (health.current > damage_amount) {
            health.current = health.current - damage_amount;
        } else {
            health.current = 0;
            
            // Add death marker for cleanup systems
            world::add_component(world, target_entity, DeadTag {});
            
            // Emit death event
            event::emit(PlayerDeathEvent {
                entity: target_entity,
                killer: tx_context::sender(ctx),
            });
        };
        
        // Emit damage event for frontend
        event::emit(DamageDealtEvent {
            target: target_entity,
            damage: damage_amount,
            remaining_health: health.current,
        });
    }
}
4

Build and Test

# Build contracts
dubhe build

# Run unit tests
dubhe test

# Start local blockchain for integration testing
dubhe node start

# Deploy to local network
dubhe deploy --network local

# Run integration tests
dubhe test --integration

๐Ÿงฉ Core ECS Concepts in Practice

Components: Pure Data Containers

Components should be small, focused data structures with no behavior.
// โœ… Good: Small, focused components
struct HealthComponent has store, drop {
    current: u64,
    maximum: u64,
}

struct PositionComponent has store, drop {
    x: u64,
    y: u64,
}

struct VelocityComponent has store, drop {
    dx: u64,  // velocity in x direction
    dy: u64,  // velocity in y direction
}

// โŒ Bad: Large, unfocused component
struct MegaPlayerComponent has store, drop {
    // Too many different concepts in one component
    health: u64,
    max_health: u64,
    position_x: u64,
    position_y: u64,
    velocity_x: u64,
    velocity_y: u64,
    level: u8,
    experience: u64,
    inventory_items: vector<u64>,
    equipment_weapon: u64,
    equipment_armor: u64,
    // ... this becomes unmaintainable
}

Systems: Pure Logic Functions

Systems are functions that operate on entities with specific component combinations.
module game::movement_system {
    use game::world::{Self, World};
    use game::components::{PositionComponent, VelocityComponent};
    
    // System processes all entities with Position + Velocity
    public entry fun update_movement(world: &mut World) {
        // This would be generated by Dubhe's query system
        let moving_entities = world::query_entities_with<PositionComponent, VelocityComponent>(world);
        
        let i = 0;
        while (i < vector::length(&moving_entities)) {
            let entity = *vector::borrow(&moving_entities, i);
            
            // Get components
            let position = world::get_mut_component<PositionComponent>(world, entity);
            let velocity = world::get_component<VelocityComponent>(world, entity);
            
            // Apply physics
            position.x = position.x + velocity.dx;
            position.y = position.y + velocity.dy;
            
            // Boundary checking
            if (position.x > MAX_WORLD_X) position.x = MAX_WORLD_X;
            if (position.y > MAX_WORLD_Y) position.y = MAX_WORLD_Y;
            
            i = i + 1;
        };
    }
    
    // System for player-initiated movement
    public entry fun move_entity(
        world: &mut World,
        entity: u64,
        new_x: u64,
        new_y: u64,
        ctx: &TxContext
    ) {
        // Validate ownership
        let player = world::get_component<PlayerComponent>(world, entity);
        assert!(player.owner == tx_context::sender(ctx), ENotOwner);
        
        // Validate position bounds
        assert!(new_x <= MAX_WORLD_X && new_y <= MAX_WORLD_Y, EInvalidPosition);
        
        // Update position
        let position = world::get_mut_component<PositionComponent>(world, entity);
        position.x = new_x;
        position.y = new_y;
        
        // Emit movement event
        event::emit(EntityMovedEvent {
            entity,
            old_x: position.x,
            old_y: position.y,
            new_x,
            new_y,
        });
    }
}

๐Ÿ“š Detailed Tutorial Series

๐Ÿ—๏ธ Step-by-Step Contract Development

Part 1: Project Setup

15 minutes โ€ข Initialize your Dubhe project, configure development environment, and understand the project structure.What youโ€™ll learn:
  • Project initialization and configuration
  • Development environment setup
  • Understanding generated project structure
  • Configuring for multiple blockchain networks

Part 2: Local Development

10 minutes โ€ข Start a local blockchain node for rapid development and testing.What youโ€™ll learn:
  • Starting local Sui/Aptos nodes
  • Network configuration and connection
  • Account management and funding
  • Basic deployment workflow

Part 3: Contract Implementation

30 minutes โ€ข Implement your first ECS-based game contracts with components and systems.What youโ€™ll learn:
  • Schema design and code generation
  • Component implementation patterns
  • System function development
  • Event emission for frontend integration

Part 4: Testing Strategies

25 minutes โ€ข Write comprehensive tests for your Move contracts using Dubheโ€™s testing framework.What youโ€™ll learn:
  • Unit testing individual components and systems
  • Integration testing with complete game scenarios
  • Test data setup and teardown
  • Performance and gas testing

Part 5: Deployment

20 minutes โ€ข Deploy your contracts to testnet and mainnet with proper verification.What youโ€™ll learn:
  • Testnet deployment and verification
  • Mainnet deployment best practices
  • Contract verification on explorers
  • Post-deployment monitoring setup

๐ŸŽฎ Practical Examples

Example 1: Simple Combat System

# Combat system schema
components:
  HealthComponent:
    current: u64
    maximum: u64
  
  AttackComponent:
    damage: u64
    range: u64
    cooldown: u64
  
  DefenseComponent:
    armor: u64
    resistance: u8  # 0-100 percentage

systems:
  - CombatSystem

events:
  - DamageDealt
  - PlayerDeath
  - CombatAction

Example 2: Inventory Management

components:
  InventoryComponent:
    items: "vector<u64>"  # Item entity IDs
    capacity: u32
  
  ItemComponent:
    name: string
    item_type: u8
    stack_size: u32
    rarity: u8
  
  EquipmentComponent:
    weapon: "Option<u64>"
    armor: "Option<u64>" 
    accessory: "Option<u64>"

systems:
  - InventorySystem
  - EquipmentSystem

๐Ÿงช Testing Your Contracts

Unit Testing Approach

#[test_only]
module game::test_components {
    use game::components::{HealthComponent, AttackComponent};
    
    #[test]
    fun test_health_component_creation() {
        let health = HealthComponent {
            current: 100,
            maximum: 100,
        };
        
        assert!(health.current == 100, 0);
        assert!(health.maximum == 100, 0);
    }
    
    #[test]
    fun test_attack_component_values() {
        let attack = AttackComponent {
            damage: 25,
            range: 5,
            cooldown: 1000, // 1 second in milliseconds
        };
        
        assert!(attack.damage == 25, 0);
        assert!(attack.range == 5, 0);
        assert!(attack.cooldown == 1000, 0);
    }
}

Integration Testing

// TypeScript integration tests
import { DubheClient, TestClient } from '@0xobelisk/dubhe-client';

describe('Contract Integration Tests', () => {
  let client: TestClient;
  
  beforeEach(async () => {
    client = await TestClient.setup();
  });
  
  it('should create player and attack monster', async () => {
    // Create player entity
    const player = await client.createEntity();
    await client.setComponent(player, 'HealthComponent', {
      current: 100n,
      maximum: 100n
    });
    await client.setComponent(player, 'AttackComponent', {
      damage: 25n,
      range: 1n,
      cooldown: 0n
    });
    
    // Create monster entity
    const monster = await client.createEntity();
    await client.setComponent(monster, 'HealthComponent', {
      current: 50n,
      maximum: 50n
    });
    
    // Execute attack
    await client.tx.combatSystem.attack({
      attacker: player,
      target: monster
    });
    
    // Verify results
    const monsterHealth = await client.getComponent('HealthComponent', monster);
    expect(monsterHealth.current).toBe(25n); // 50 - 25 = 25
  });
});

๐Ÿš€ Advanced Patterns

State Machines with Components

// AI states as components
struct IdleState has store, drop {
    idle_duration: u64,
}

struct PatrolState has store, drop {
    current_waypoint: u64,
    patrol_path: vector<u64>,
}

struct AttackState has store, drop {
    target_entity: u64,
    attack_start_time: u64,
}

// AI system processes entities based on their current state
public entry fun ai_system(world: &mut World) {
    // Process idle entities
    let idle_entities = world::query_entities_with<IdleState>(world);
    process_idle_ai(world, idle_entities);
    
    // Process patrolling entities
    let patrol_entities = world::query_entities_with<PatrolState>(world);
    process_patrol_ai(world, patrol_entities);
    
    // Process attacking entities
    let attack_entities = world::query_entities_with<AttackState>(world);
    process_attack_ai(world, attack_entities);
}

Event-Driven Architecture

// Events for system coordination
struct QuestCompletedEvent has copy, drop {
    player: u64,
    quest_id: u64,
    rewards: vector<u64>,
}

struct LevelUpEvent has copy, drop {
    player: u64,
    new_level: u8,
    skill_points: u8,
}

// Systems can react to events
public entry fun reward_system(world: &mut World) {
    // Process quest completion rewards
    // This would integrate with event listening in a real implementation
}

๐Ÿ’ก Best Practices

Security Guidelines

Access Control

Always validate entity ownership before modifying components

Input Validation

Validate all function parameters and assert preconditions

Resource Management

Use Moveโ€™s resource safety to prevent duplication and loss

Event Emission

Emit events for all important state changes

Performance Optimization

1

Minimize Component Size

Keep components small to reduce gas costs
// โœ… Good: Small, focused component
struct PositionComponent has store, drop {
    x: u64, // 8 bytes
    y: u64, // 8 bytes
} // Total: 16 bytes

// โŒ Bad: Large component with unused fields
struct MegaComponent has store, drop {
    // Too much data in one component
    // Expensive to load when you only need position
}
2

Batch Operations

Process multiple entities in single function calls when possible
3

Optimize Queries

Structure your component queries for maximum efficiency

๐ŸŽฏ Next Steps

Start the Tutorial Series

Begin with project setup and work through each step

Join the Community

Get help from other developers and share your projects

Explore Examples

See a complete game implementation using these concepts

Advanced Concepts

Deep dive into ECS architecture patterns