Skip to main content

Smart Contract Interfaces

Dubhe Engine smart contracts are built using the Move programming language, providing type-safe, resource-oriented programming for blockchain applications.

Component Contracts

Base Component Structure

All components in Dubhe Engine follow a standardized Move struct pattern:
module game::player_component {
    use sui::object::{Self, UID};
    use sui::tx_context::TxContext;

    public struct PlayerComponent has key, store {
        id: UID,
        health: u64,
        level: u8,
        experience: u64,
        position_x: u64,
        position_y: u64,
    }

    public fun new(
        health: u64,
        level: u8,
        ctx: &mut TxContext
    ): PlayerComponent {
        PlayerComponent {
            id: object::new(ctx),
            health,
            level,
            experience: 0,
            position_x: 0,
            position_y: 0,
        }
    }
}

Component Operations

Create Component

public entry fun create_player_component(
    health: u64,
    level: u8,
    ctx: &mut TxContext
) {
    let component = PlayerComponent {
        id: object::new(ctx),
        health,
        level,
        experience: 0,
        position_x: 0,
        position_y: 0,
    };
    
    transfer::share_object(component);
}

Update Component

public entry fun update_player_health(
    player: &mut PlayerComponent,
    new_health: u64
) {
    player.health = new_health;
}

System Contracts

System Function Pattern

System functions operate on components and implement game logic:
module game::player_system {
    use game::player_component::{Self, PlayerComponent};
    
    // Error codes
    const EInsufficientHealth: u64 = 1;
    const EMaxLevelReached: u64 = 2;
    const EInsufficientExperience: u64 = 3;

    public entry fun level_up(
        player: &mut PlayerComponent,
        ctx: &mut TxContext
    ) {
        let current_level = player_component::level(player);
        let current_exp = player_component::experience(player);
        let required_exp = calculate_required_exp(current_level);
        
        assert!(current_exp >= required_exp, EInsufficientExperience);
        assert!(current_level < 100, EMaxLevelReached);
        
        player_component::set_level(player, current_level + 1);
        player_component::set_experience(player, current_exp - required_exp);
        
        // Emit level up event
        event::emit(PlayerLevelUpEvent {
            player_id: object::id(player),
            new_level: current_level + 1,
        });
    }
    
    public entry fun move_player(
        player: &mut PlayerComponent,
        delta_x: u64,
        delta_y: u64
    ) {
        let current_x = player_component::position_x(player);
        let current_y = player_component::position_y(player);
        
        player_component::set_position_x(player, current_x + delta_x);
        player_component::set_position_y(player, current_y + delta_y);
    }
    
    fun calculate_required_exp(level: u8): u64 {
        (level as u64) * 100 + ((level as u64) * (level as u64)) * 10
    }
}

Event System

Event Definitions

module game::events {
    use sui::object::ID;
    
    public struct PlayerLevelUpEvent has copy, drop {
        player_id: ID,
        new_level: u8,
    }
    
    public struct PlayerMoveEvent has copy, drop {
        player_id: ID,
        from_x: u64,
        from_y: u64,
        to_x: u64,
        to_y: u64,
    }
    
    public struct ItemCraftedEvent has copy, drop {
        player_id: ID,
        item_type: u8,
        quantity: u64,
    }
}

Access Control

Permission Patterns

module game::admin {
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};
    
    public struct AdminCap has key, store {
        id: UID,
    }
    
    public struct GameConfig has key {
        id: UID,
        max_level: u8,
        exp_multiplier: u64,
        admin: address,
    }
    
    public entry fun init_game(ctx: &mut TxContext) {
        let admin_cap = AdminCap {
            id: object::new(ctx),
        };
        
        let config = GameConfig {
            id: object::new(ctx),
            max_level: 100,
            exp_multiplier: 100,
            admin: tx_context::sender(ctx),
        };
        
        transfer::transfer(admin_cap, tx_context::sender(ctx));
        transfer::share_object(config);
    }
    
    public entry fun update_max_level(
        _admin_cap: &AdminCap,
        config: &mut GameConfig,
        new_max_level: u8
    ) {
        config.max_level = new_max_level;
    }
}

Error Handling

Standard Error Codes

CodeNameDescription
1EInsufficientPermissionCaller lacks required permissions
2EInvalidInputInput parameters are invalid
3EResourceNotFoundRequested resource doesn’t exist
4EInsufficientBalanceInsufficient balance for operation
5EGameLogicViolationOperation violates game rules

Error Usage

public entry fun transfer_item(
    from_player: &mut PlayerComponent,
    to_player: &mut PlayerComponent,
    item_id: u64,
    quantity: u64
) {
    assert!(quantity > 0, EInvalidInput);
    
    let from_inventory = player_component::inventory(from_player);
    let item_count = inventory::get_item_count(from_inventory, item_id);
    
    assert!(item_count >= quantity, EInsufficientBalance);
    
    inventory::remove_item(from_inventory, item_id, quantity);
    inventory::add_item(
        player_component::inventory_mut(to_player), 
        item_id, 
        quantity
    );
}

Testing Patterns

Unit Test Example

#[test_only]
module game::player_system_tests {
    use game::player_system;
    use game::player_component;
    use sui::test_scenario;
    
    #[test]
    fun test_level_up_success() {
        let admin = @0xADMIN;
        let scenario_val = test_scenario::begin(admin);
        let scenario = &mut scenario_val;
        
        // Create player with sufficient experience
        let player = player_component::new_for_testing(100, 1, 150);
        
        // Level up should succeed
        player_system::level_up(&mut player);
        
        assert!(player_component::level(&player) == 2, 0);
        assert!(player_component::experience(&player) == 40, 1);
        
        test_scenario::end(scenario_val);
    }
    
    #[test]
    #[expected_failure(abort_code = player_system::EInsufficientExperience)]
    fun test_level_up_insufficient_exp() {
        let admin = @0xADMIN;
        let scenario_val = test_scenario::begin(admin);
        let scenario = &mut scenario_val;
        
        // Create player with insufficient experience
        let player = player_component::new_for_testing(100, 1, 50);
        
        // This should fail
        player_system::level_up(&mut player);
        
        test_scenario::end(scenario_val);
    }
}

Best Practices

Security Guidelines
  1. Always validate inputs before processing
  2. Use proper access controls for administrative functions
  3. Emit events for important state changes
  4. Handle edge cases explicitly
  5. Write comprehensive tests for all functions

Next Steps

Client SDK API

Learn to interact with contracts from frontend

Schema Definitions

Understand component data structures