Skip to content

Latest commit

 

History

History
161 lines (109 loc) · 5.13 KB

memory.md

File metadata and controls

161 lines (109 loc) · 5.13 KB

MEMORY in the Ethereum Virtual Machine (EVM)

Memory in the Ethereum Virtual Machine (EVM) is a temporary, byte-addressable storage area that exists only during the execution of a smart contract. Understanding how memory operates is essential for writing efficient and secure Solidity contracts.


1. Characteristics of Memory

  1. Byte Addressable:

    • Memory is organized as a large array of bytes, which means each byte can be accessed individually.
  2. Dynamic Size:

    • Memory starts empty and expands dynamically as needed. The cost of memory expansion increases with the size of the memory accessed.
  3. Temporary Nature:

    • Memory is cleared after the function execution. It is not persistent, unlike storage.
  4. Word Size:

    • Memory operations generally involve 32-byte (256-bit) words.

2. Reserved Memory Layout

Solidity reserves specific areas in memory for internal operations:

  1. Scratch Space:

    • The first 64 bytes (0x00 to 0x3F) are used as scratch space for intermediate calculations (e.g., by the keccak256 function).
  2. Free Memory Pointer:

    • Located at 0x40 (64th byte). It stores the address of the first unused memory slot. Memory allocations should always reference and update this pointer.
  3. Empty Space:

    • The region between 0x60 and the first free memory slot is reserved and not actively used.

3. Key Memory Operations

Memory is manipulated using specific EVM opcodes:

3.1 MSTORE

  • Stores a 32-byte word at a specific memory location.
  • Opcode: MSTORE.

Example:

PUSH1 0x10    // Value to store (16 in decimal)
PUSH1 0x00    // Memory address
MSTORE        // Store 0x10 at memory address 0x00

3.2 MLOAD

  • Loads a 32-byte word from a specific memory location.
  • Opcode: MLOAD.

Example:

PUSH1 0x00    // Memory address
MLOAD         // Load the value at address 0x00

3.3 MSTORE8

  • Stores a single byte at a specific memory location.
  • Opcode: MSTORE8.

Example:

PUSH1 0xFF    // Value to store (255 in decimal)
PUSH1 0x00    // Memory address
MSTORE8       // Store 0xFF at memory address 0x00

4. Memory Expansion Costs

Memory expansion is not free and depends on the highest memory address accessed during a transaction. Key points:

  1. Linear Growth (First 724 Bytes):

    • Gas costs grow linearly for memory accessed up to 724 bytes.
  2. Exponential Growth Beyond 724 Bytes:

    • Accessing memory beyond 724 bytes causes gas costs to increase exponentially.
  3. Implications:

    • Avoid allocating excessively large memory arrays unless absolutely necessary.

5. Memory Layout for Data Types

5.1 Value Types

  • Value types (e.g., uint256, bool, address) take up 32 bytes in memory.

Example:

uint256 x = 10; // Stored as a single 32-byte word in memory

5.2 Reference Types

  • Reference types (e.g., arrays, structs) have more complex memory layouts:

    • Static Arrays: Start with the length, followed by the elements.
    • Dynamic Arrays and Strings: Start with the length, followed by a pointer to the actual data.

    For a detailed implementation, refer to the DynamicArraysInStorage.sol file in the repository, which includes examples and assembly functions to explore dynamic arrays in Memory.

Example:

string memory example = "Hello"; // Length (5) + Characters (bytes of 'Hello')

6. Practical Tips for Using Memory

  1. Use Memory for Temporary Data:

    • Memory is ideal for temporary computations and passing data between functions.
  2. Prefer Calldata for Read-Only Data:

    • When possible, use calldata for read-only function arguments, as it is cheaper than memory.
  3. Avoid Excessive Memory Expansion:

    • Minimize operations that access high memory addresses to control gas costs.
  4. Update the Free Memory Pointer:

    • Always adjust the free memory pointer (MSTORE 0x40) when allocating new memory.

7. Example Solidity Code

This Solidity example demonstrates memory allocation and usage:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MemoryExample {
    function allocateMemory() external pure returns (bytes memory) {
        bytes memory temp = new bytes(10); // Allocate 10 bytes in memory
        for (uint256 i = 0; i < temp.length; i++) {
            temp[i] = bytes1(uint8(i + 65)); // Fill with ASCII characters (A-J)
        }
        return temp;
    }
}

8. Resources for Learning


Understanding memory in the EVM is critical for writing efficient, gas-optimized contracts. By following best practices and leveraging memory effectively, developers can create secure and performant smart contracts.