Volver a la página principal
martes 10 diciembre 2024
7

Uso de storage, memory y calldata en Solidity para especificar la ubicación de los datos

Ubicaciones de datos en Solidity

1. storage:

  • Representa la memoria permanente del contrato.
  • Los datos almacenados en storage persisten en la blockchain.
  • Las operaciones en storage son costosas debido al almacenamiento permanente.

2. memory:

  • Es una memoria temporal utilizada durante la ejecución de una función.
  • Los datos en memory existen solo mientras se ejecuta la función y se eliminan después.
  • Más barata que storage.

3. calldata:

  • Es una ubicación de solo lectura para los datos de entrada de las funciones.
  • Más eficiente en términos de gas porque no permite modificaciones.
  • Solo aplicable a funciones públicas y externas.

Declaración de ubicaciones

La ubicación de los datos debe especificarse explícitamente para los tipos complejos como arrays y structs cuando se utilizan en funciones.

function miFuncion(uint[] memory array) public {
    // Lógica de la función
}

Ejemplos prácticos

1. Uso de storage

storage se utiliza para manipular variables de estado del contrato.

contract EjemploStorage {
    uint[] public numeros;

    function agregarNumero(uint _numero) public {
        numeros.push(_numero);
    }

    function actualizarNumero(uint indice, uint nuevoValor) public {
        uint[] storage referencia = numeros; // Referencia directa a storage
        referencia[indice] = nuevoValor;
    }
}

En este ejemplo:

  • numeros es una variable de estado almacenada en storage.
  • referencia apunta directamente a numeros y modifica su contenido en la blockchain.

2. Uso de memory

memory es útil para manejar datos temporales y evitar modificar variables de estado.

contract EjemploMemory {
    function duplicarArray(uint[] memory array) public pure returns (uint[] memory) {
        uint[] memory nuevoArray = new uint[](array.length);
        for (uint i = 0; i < array.length; i++) {
            nuevoArray[i] = array[i] * 2;
        }
        return nuevoArray;
    }
}

En este caso:

  • array es un parámetro temporal en memory.
  • nuevoArray se crea en memory y se devuelve sin afectar el estado del contrato.

3. Uso de calldata

calldata es ideal para datos de solo lectura en funciones públicas y externas.

contract EjemploCalldata {
    function concatenarStrings(string calldata a, string calldata b) public pure returns (string memory) {
        return string(abi.encodePacked(a, b));
    }
}

En este ejemplo:

  • a y b son parámetros de solo lectura almacenados en calldata.
  • No se permite modificar directamente a o b.

Comparación de storage, memory y calldata

Atributo storage memory calldata
Ubicación Blockchain (permanente) Memoria temporal (RAM del EVM) Datos de entrada de solo lectura
Costo Alto (operaciones en blockchain) Medio (temporal, más barato que storage) Bajo (eficiente en gas)
Mutabilidad No
Duración Persiste entre transacciones Solo durante la ejecución Solo durante la ejecución

Casos de uso recomendados

1. Usa storage para:

  • Variables de estado.
  • Datos que deben persistir entre transacciones.
uint[] public listaPersistente; // Se almacena en storage

2. Usa memory para:

  • Datos temporales y modificables en el contexto de una función.
  • Arrays y structs que no necesitan persistir.
function procesarTemporalmente(uint[] memory datos) public pure {}

3. Usa calldata para:

  • Datos de entrada que no requieren modificación.
  • Optimizar el consumo de gas al trabajar con grandes volúmenes de datos de solo lectura.
function procesarDatos(string calldata datos) public {}

Ejemplo avanzado: Combinando ubicaciones

contract ManejoUbicaciones {
    uint[] public lista;

    // Agregar elementos a la lista
    function agregar(uint valor) public {
        lista.push(valor);
    }

    // Multiplicar valores de la lista sin modificarla
    function multiplicar(uint multiplicador) public view returns (uint[] memory) {
        uint[] memory copiaLista = lista; // Copia en memoria
        for (uint i = 0; i < copiaLista.length; i++) {
            copiaLista[i] *= multiplicador;
        }
        return copiaLista;
    }

    // Actualizar elementos directamente
    function actualizar(uint indice, uint valor) public {
        uint[] storage referencia = lista; // Referencia en storage
        referencia[indice] = valor;
    }
}

Consideraciones importantes

1. Especificar la ubicación explícitamente:

  • Solidity requiere que se indique la ubicación para arrays y structs cuando se usan como parámetros o variables locales.

2. Optimización de gas:

  • Usa calldata para parámetros de solo lectura en funciones externas.
  • Minimiza el uso de storage para reducir costos.

3. Evita copiar grandes estructuras:

  • Copiar datos de storage a memory puede ser costoso en términos de gas.

Buenas prácticas

  • Usa storage para datos persistentes que requieran modificaciones entre transacciones.
  • Usa memory para operaciones temporales que no necesiten persistir.
  • Usa calldata para manejar parámetros de entrada que no necesiten ser modificados.

Referencias oficiales

Para más información sobre storage, memory y calldata, consulta la documentación oficial de Solidity.

Etiquetas:
solidity
Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer