> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peaq.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Optimizing Storage

Storage **optimization** in Solidity smart contracts is an essential consideration given the relatively high costs associated with writing data to the blockchain.
Every write operation has a **gas fee**, and more extensive storage usage equates to **increased** deployment and execution costs. The peaq network provides EVM
compatibility, so the same best practices that apply to Ethereum also apply here—but within peaq's unique economy and infrastructure.

**Key topics covered in this guide include:**

* How the EVM (on peaq) stores data in 256-bit storage slots.
* Why **mappings** can be more efficient than arrays in certain scenarios.
* Additional strategies like data packing, using `bytes` effectively, and using events instead of on-chain storage.
* Practical coding examples in Solidity.

## Prerequisites

* Basic Solidity Knowledge
* An understanding of how the EVM handles storage, memory, and call data, as well as the associated gas costs for these operations.
* Familiar that peaq network is EVM-compatible and that the best practices for storage optimization hold true in its environment, with potential additional benefits from the underlying Substrate-based infrastructure.

## Optimizations

Below are some high-level strategies and examples to help you optimize your smart contract storage usage on the peaq network's EVM.

### Use Mappings Instead of Arrays (When Feasible)

**Arrays** (especially dynamic arrays) often require more operations to manage (e.g., iteration, boundary checks) and can grow
unbounded if not carefully restricted. **Mappings** store data more sparsely, **saving storage** when you do not need sequential elements.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
// Example: Using a mapping instead of a dynamic array

// Less optimal: using a dynamic array
contract TestArray {
    uint256[] public items;
    
    function addItem(uint256 value) external {
        items.push(value);
    }
    
    // Accessing an element
    function getItem(uint256 index) external view returns (uint256) {
        return items[index];
    }
}

// More optimal: using a mapping
contract TestMapping {
    mapping(uint256 => uint256) public items;
    uint256 public itemCount;
    
    function addItem(uint256 value) external {
        items[itemCount] = value;
        itemCount++;
    }
    
    // Accessing an element
    function getItem(uint256 index) external view returns (uint256) {
        return items[index];
    }
}
```

**Explanation**:

* In the above example, `TestMapping` is more flexible with sparse data and can save gas.
* You can maintain a separate counter (`itemCount`) to track indices, mimicking array-like behavior.

### Use the Right Data Types and Pack Them

The EVM stores data in 256-bit slots. When two or more variables fit into a **single** 256-bit slot, they can be **packed together** to reduce overall storage usage.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
contract DataPacking {
    // All of these will fit into a single 256-bit slot if ordered properly
    uint128 public valueA; // 128 bits
    uint64  public valueB; // 64 bits
    uint64  public valueC; // 64 bits
    
    // Another slot
    address public owner;   // 160 bits
    bool    public isActive; // 8 bits
    uint88  public counter;  // 88 bits
}
```

**Best Practice**:

* Group smaller types **together** so they can occupy the same slot.
* Reordering the variables to **minimize** wasted space in each 256-bit slot can substantially reduce gas costs.

### Store Data Off-Chain or Use Events When Appropriate

If you only need data for historical or informational purposes (i.e., you don't need it to stay in contract storage for on-chain logic), consider storing it **off-chain** or **emitting**
it in **events**. Events are cheaper than storing data on-chain and still let you retrieve the data from transaction logs.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
contract UseEvents {
    // Instead of storing all these logs on-chain...
    // mapping(uint256 => string) public logs;  // This can grow unbounded
    
    // Emit an event to record data
    event LogRecorded(uint256 indexed id, string info);
    
    function recordLog(uint256 _id, string memory _info) external {
        emit LogRecorded(_id, _info);
    }
}

```

**Note**:

* Data in events is not accessible to contracts directly (only via off-chain **indexing**), so only move data to events if your contract does not need to rely on it for future state changes.
* Checkout our [indexing solutions](/peaqchain/build/advanced-operations/indexing) to see how to query these events.

### Consider Using `bytes` Arrays for Packed Storage

In some scenarios (especially with strings or variable-length data), storing in a single `bytes` array can be more **efficient** than storing an array of fixed-size data types, as it packs data continuously without leaving empty space.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
contract BytesStorage {
    bytes public data; 
    
    function appendData(bytes memory newData) external {
        // Append in memory, then store
        data = abi.encodePacked(data, newData);
    }
}
```

### Carefully Manage Storage Reads and Writes

Each storage write operation **costs gas**, so **optimizing** writes—and reducing the number of expensive `SSTORE` operations—can lower costs.

**Strategies**:

* **Minimize writes** by caching frequently updated values in memory and writing only once when necessary.
* **Use local variables** (memory) rather than reading from storage multiple times in a function. Each storage read is more expensive than a memory read.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
contract StorageWrites {
    uint256 public counter;
    
    function incrementCounter(uint256 times) external {
        uint256 temp = counter; // Only one storage read
        for (uint256 i = 0; i < times; i++) {
            temp++;  // Increment in memory
        }
        counter = temp; // Single storage write
    }
}
```

### Reset Storage Slots to Zero When Possible

The EVM provides a **gas refund** for clearing storage slots (i.e., writing zero to a previously non-zero slot). Although refunds are capped,
this can still result in **net savings** when performing large transactions.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
contract StorageCleanup {
    mapping(address => uint256) public balances;
    
    function withdrawAll() external {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance");
        
        // Clear the slot
        balances[msg.sender] = 0;
        
        // Transfer or other business logic
        // ...
    }
}
```

### Use Structs Wisely

Grouping related variables into **structs** can help you manage your data more systematically. However, remember that each struct field occupies
storage in **256-bit slots**, and you can still leverage the same packing principles within a struct.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
struct UserInfo {
    uint8 age;
    uint64 score;
    bool isActive;
    // Note: reordering could pack these into fewer slots
}

contract ManageUsers {
    mapping(address => UserInfo) public users;
    
    function setUserInfo(uint8 _age, uint64 _score, bool _active) external {
        // Single store operation if we read the struct first into memory, modify it, then write once
        UserInfo memory tempUser = users[msg.sender];
        tempUser.age = _age;
        tempUser.score = _score;
        tempUser.isActive = _active;
        
        users[msg.sender] = tempUser;
    }
}
```

## Summary

Optimizing storage in Solidity smart contracts not only **reduces** gas costs for your end-users but also contributes to the overall **efficiency** of the peaq network.
By adopting these best practices—using mappings, packing data, leveraging events instead of persistent storage, and resetting unused slots—you can ensure your dApps remain economical and performant.

**Key Takeaways**:

* **Mappings** often outperform arrays, especially for sparse data.
* **Data Packing** aligns smaller data types to minimize wasted storage space.
* **Events** are a cheaper alternative to on-chain storage if you only need data for off-chain retrieval.
* **Minimizing Writes** to contract storage lowers gas costs significantly.
* **Storage Slot Cleanup** can yield gas refunds and improve contract efficiency.

As you develop on the peaq network's EVM, keep these strategies in mind to create secure, cost-effective, and efficient smart contracts.
By following these guidelines, you will **optimize** your contracts' storage usage, reducing costs and enhancing the user experience for your dApps.
