Skip to content

feat: Missing Gas Optimization and Performance Improvements #148

@KevinMB0220

Description

@KevinMB0220

Issue: Missing Gas Optimization and Performance Improvements

Description:

The codebase lacks gas optimization strategies and performance improvements. Many functions perform inefficient operations, have unnecessary storage reads/writes, and don't implement batch operations where they would be beneficial. This leads to high gas costs and poor user experience.

Problems Identified:

  1. Inefficient Storage Operations: Multiple unnecessary storage reads and writes in loops
  2. Missing Batch Operations: No batch processing for multiple items or operations
  3. Redundant Calculations: Same calculations performed multiple times
  4. Inefficient Array Operations: Array operations that could be optimized
  5. Missing Gas Optimization Patterns: No use of gas-saving techniques like storage packing

Code Issues Found:

// In PlayerActions - Line 143: Inefficient loop with multiple storage reads
loop {
    if target_index >= target.len() {
        break;
    }
    let target_id = *target.at(target_index);
    let target_type = *target_types.at(target_index);
    
    // Multiple storage reads in loop - inefficient
    let player: Player = world.read_model(caller); // Read every iteration
    let faction_stats = self.get_faction_stats(player.faction); // Recalculated every iteration
    
    // ... damage calculation
    target_index += 1;
};

// In GearActions - Line 1080: Inefficient array iteration
while current_id <= max_checks && result_items.len() < limit {
    let gear: Gear = world.read_model(current_id); // Storage read in loop
    // ... processing
    current_id += 1;
};

Specific Problems:

  1. Multiple Storage Reads: Functions read the same data multiple times
  2. Inefficient Loops: Loops that could be optimized or batched
  3. Redundant Calculations: Same calculations repeated unnecessarily
  4. Missing Caching: No caching of frequently accessed data
  5. Inefficient Array Operations: Array operations that could be optimized

Impact:

  • Medium Priority: High gas costs for users
  • Poor user experience due to expensive transactions
  • Inefficient use of blockchain resources
  • Potential for transaction failures due to gas limits
  • Higher operational costs

Recommended Solutions:

  1. Optimize Storage Operations:

    fn deal_damage_optimized(
        ref self: ContractState,
        target: Array<u256>,
        target_types: Array<felt252>,
        with_items: Array<u256>,
        session_id: felt252,
    ) {
        // Validate session once
        self.validate_session_for_action(session_id);
        
        let mut world = self.world_default();
        let caller = get_caller_address();
        
        // Read player data once outside the loop
        let player: Player = world.read_model(caller);
        let faction_stats = self.get_faction_stats(player.faction);
        
        // Pre-calculate base damage once
        let base_damage = if with_items.len() == 0 {
            self.calculate_melee_damage(player.clone(), faction_stats)
        } else {
            self.calculate_weapon_damage(player.clone(), with_items.span(), faction_stats)
        };
        
        // Process all targets efficiently
        let mut target_index = 0;
        while target_index < target.len() {
            let target_id = *target.at(target_index);
            let target_type = *target_types.at(target_index);
            
            // Use pre-calculated damage
            self.damage_target(target_id, target_type, base_damage);
            
            target_index += 1;
        };
    }
  2. Implement Batch Operations:

    fn batch_equip_gear(ref self: ContractState, item_ids: Array<u256>, session_id: felt252) {
        self.validate_session_for_action(session_id);
        let caller = get_caller_address();
        let mut world = self.world_default();
        
        // Read player data once
        let mut player: Player = world.read_model(caller);
        
        // Process all items in batch
        let mut i = 0;
        while i < item_ids.len() {
            let item_id = *item_ids.at(i);
            
            // Validate item ownership once
            if self.validate_item_ownership(caller, item_id) {
                // Equip item
                player.equip(item_id);
            }
            
            i += 1;
        };
        
        // Write player data once
        world.write_model(@player);
    }
  3. Optimize Array Operations:

    fn get_gear_details_batch_optimized(
        ref self: ContractState, 
        item_ids: Array<u256>, 
        session_id: felt252
    ) -> Array<GearDetailsComplete> {
        if !self.validate_session_for_read_action(session_id) {
            return array![];
        }
        
        let world = self.world_default();
        let mut results = array![];
        
        // Pre-allocate array if possible
        let mut i = 0;
        while i < item_ids.len() {
            let item_id = *item_ids.at(i);
            
            // Batch read gear data
            let gear: Gear = world.read_model(item_id);
            
            if gear.id != 0 {
                // Calculate stats efficiently
                let calculated_stats = self._calculate_gear_stats(@gear);
                let ownership_status = self._get_ownership_status(@gear);
                
                let gear_details = GearDetailsComplete {
                    gear,
                    calculated_stats,
                    upgrade_info: Option::None, // Skip for performance
                    ownership_status,
                };
                
                results.append(gear_details);
            }
            
            i += 1;
        };
        
        results
    }
  4. Implement Caching for Frequently Accessed Data:

    // Cache frequently accessed data
    fn get_cached_faction_stats(self: @ContractState, faction: felt252) -> FactionStats {
        // In a real implementation, you might cache this data
        // to avoid recalculating it every time
        self.get_faction_stats(faction)
    }
    
    fn get_cached_player_data(self: @ContractState, player_id: ContractAddress) -> Player {
        // Cache player data for the duration of a transaction
        // to avoid multiple storage reads
        let world = self.world_default();
        world.read_model(player_id)
    }
  5. Optimize Storage Layout:

    // Use storage packing to reduce gas costs
    #[derive(Drop, Copy, Serde, Default)]
    pub struct OptimizedPlayer {
        #[key]
        pub id: ContractAddress,
        // Pack related data together
        pub hp: u256,
        pub max_hp: u256,
        pub level: u256,
        pub xp: u256,
        // Use smaller types where possible
        pub rank: u8, // Instead of PlayerRank enum
        pub faction: u8, // Instead of felt252
        pub max_equip_slot: u8, // Instead of u32
    }
  6. Implement Gas-Efficient Batch Processing:

    fn process_batch_operations(
        ref self: ContractState,
        operations: Array<GameOperation>,
        session_id: felt252,
    ) {
        self.validate_session_for_action(session_id);
        
        let mut world = self.world_default();
        let caller = get_caller_address();
        
        // Read all required data once
        let mut player: Player = world.read_model(caller);
        let faction_stats = self.get_faction_stats(player.faction);
        
        // Process all operations in batch
        let mut i = 0;
        while i < operations.len() {
            let operation = *operations.at(i);
            
            match operation.operation_type {
                'EQUIP' => {
                    // Process equipment
                    player.equip(operation.item_id);
                },
                'UNEQUIP' => {
                    // Process unequipment
                    player.unequip(operation.item_id);
                },
                'UPGRADE' => {
                    // Process upgrade
                    self.process_upgrade(operation.item_id, &mut player);
                },
                _ => {}
            }
            
            i += 1;
        };
        
        // Write all changes once
        world.write_model(@player);
    }
  7. Optimize Event Emissions:

    fn emit_batch_events(ref self: ContractState, events: Array<GameEvent>) {
        let mut world = self.world_default();
        
        // Emit all events in batch to reduce gas costs
        let mut i = 0;
        while i < events.len() {
            let event = *events.at(i);
            world.emit_event(@event);
            i += 1;
        };
    }

Files Affected:

  • src/systems/player.cairo - Optimize damage and player operations
  • src/systems/gear.cairo - Optimize gear operations and queries
  • src/systems/core.cairo - Optimize core operations
  • src/models/ - Optimize data structures for storage efficiency

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions