WEB3DEV Español

Cover image for Serie de Rayos X de Solidity: Dominando el Almacenamiento — Parte 1
Juliana Cabeza
Juliana Cabeza

Posted on • Updated on

Serie de Rayos X de Solidity: Dominando el Almacenamiento — Parte 1

Image description

¿Qué se entiende exactamente por "almacenamiento del contrato"?

Es el lugar de almacenamiento en un contrato inteligente donde todas sus Variables de Estado se almacenan en slots de almacenamiento de forma permanente.

Vamos a detallarlo.

Variables de estado, se definen en el nivel del propio contrato y residen únicamente en el Almacenamiento del Contrato.

Slots de Almacenamiento, 'lugares' o 'cuartos' en un contrato para almacenar las Variables de Estado.

Y es Permanente, ya que el valor permanece allí hasta que la próxima transacción que modifica el estado lo cambie, y luego el nuevo valor actualizado también permanece allí hasta que ocurra la próxima modificación, y así sucesivamente.

Siempre está disponible para ser leído por una EOA (Externally Owned Account/Cuenta de Propiedad Externa) y/o otro contrato inteligente, incluso después que una operación haya finalizado.

Oye... ¿"otro contrato inteligente"...?

Sí, un contrato inteligente SÓLO puede leer/escribir en su propio almacenamiento de contrato por defecto.

Cuando desee leer o escribir en otro almacenamiento de contrato (objetivo), esto SÓLO es posible cuando el contrato objetivo tiene funciones de lectura/escritura disponibles en su propio código como públicas/externas.

Esto hace que el ABI del contrato objetivo exponga funciones, las cuales entidades externas pueden acceder.

Si deseas obtener más información sobre ABI, por favor, consulta aquí.

Vale la pena mencionar aquí que, cuando una EOA lee desde el almacenamiento, es gratuito.

Sin embargo, cuesta gas cuando un contrato lee el almacenamiento como parte del código (no leído directamente por una EOA).

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

contract Storage {

   uint256 a;

   function readStorage() public view returns (uint256) {
       return a;
   }
}
Enter fullscreen mode Exit fullscreen mode

Image description

Para obtener más detalles sobre la lectura del almacenamiento (código de operación: SLOAD), consulta esto.

Ahora, vamos a la Escritura (opcode: SSTORE)

Escribir en el slot de Almacenamiento del Contrato (es decir, cambiar el estado del contrato) es la operación más costosa en términos de gas en la EVM.

Estimación del costo en gas para escribir en un slot de almacenamiento:

En resumen, es complicado.

El costo de escribir en un slot de almacenamiento depende de varios factores, como por ejemplo:

(1) si el nuevo valor almacenado es cero

¿O un valor distinto de cero?

(2) si es la primera vez que el valor del slot cambia

¿O si ya se ha cambiado antes?

(3) si ya se ha accedido al slot antes de la operación de escritura en cuestión

¿O si se accede por primera vez para escribir un nuevo valor?

Todos los pros y los contras se resumen aquí en construcciones if-else.

Te recomiendo encarecidamente que experimentes todos los diferentes escenarios que puedas imaginar en un código de ejemplo en el Remix IDE y observes/compares los costos de gas, para obtener una comprensión completa.

Personalmente, te recomendaría escribir en el almacenamiento solo cuando sea ABSOLUTAMENTE necesario y, aún en ese caso, utiliza herramientas y complementos como eth-gas-reporter o hardhat-gas-reporter para verificar los costos de gas de una transacción que involucra cambios de estado en su flujo de trabajo.

Ahora vamos a entender cómo está estructurado el Almacenamiento

Esto ayuda a crear un modelo mental adecuado del almacenamiento del contrato.

Los 2 términos clave son:

Clave y Valor

Almacenamiento del Contrato = mapeo clave-valor (y no un array).

Una clave siempre es un número de 256 bits (32 bytes) que comienza con '0'.

Según el valor máximo que puede contener, teóricamente, un número no negativo de 256 bits, el valor máximo de una clave podría ser (2**256).

Este valor es tan grande que no puedo comprenderlo cognitivamente.

¿Y tú?

Un valor es también una palabra de 32 bytes que representa el valor subyacente almacenado en un slot específico en el almacenamiento.

'Palabra'... ¿qué es eso? Consulta esto

Esto significa que un contrato puede contener (2**256) - 1 variables de estado en su almacenamiento.

¡Vaya... eso es una gran cantidad de variables de estado en un solo contrato inteligente!

Y aún más, si 2 o más variables de estado se agrupan en 1 slot (discutido a continuación), el total puede ser muucho mayor.

Este es un modelo mental que creé para mi propia comprensión:

Image description

Considera este equivalente a un estante que almacena materiales en estantes, en diferentes niveles, en un almacén de fábrica como se muestra a continuación:

No he añadido la imagen del título sin motivo, tiene un propósito.

Image description

Considera el estante vertical de color azul (destacado en rojo) como el área de almacenamiento de un contrato.

Dentro de este estante, tenemos bases/estantes naranjas (destacadas en amarillo), que son los slots.

Los slots son referenciados por números clave, las mismas claves explicadas anteriormente.

Las cajas/paletas marrones (destacadas en rosa claro) son las variables de estado/almacenamiento.

Ahora vamos a entender cómo se organizan las Variables de Estado en el almacenamiento.

Todas las variables de estado de tamaño fijo se almacenan de forma contigua en el almacenamiento, en el orden de su declaración, en el contrato inteligente.

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

contract Storage {

   uint256 a = 10;
   uint256 b;
   uint128 c = 30;
   uint128 d = 40;
   uint256 e = 50;
   string storageName;

   function readStorageSlot() public view
   returns(
   bytes32 valueAtSlotZero,
   bytes32 valueAtSlotOne,
   bytes32 valueAtSlotTwo,
   bytes32 valueAtSlotThree,
   bytes32 valueAtSlotFour) {

       assembly {
           valueAtSlotZero := sload(0)
           valueAtSlotOne := sload(1)
           valueAtSlotTwo := sload(2)
           valueAtSlotThree := sload(3)
           valueAtSlotFour := sload(4)
       }
   }

   function storeString(string memory memoryName) public {
       storageName = memoryName;
   }
}

Enter fullscreen mode Exit fullscreen mode

Image description

En el código de ejemplo anterior, el valor uint256(= 10) de la variable de estado a se almacena en el**slot # 0**, ocupando todo el espacio de palabra de 32 bytes de ese slot (uint256 = 256 bits = 32 bytes).

Luego, el valor uint256de b(= 0) en el slot # 1.

Y después, el slot # 2 agrupa dos variables de valor (= 30, 40) uint128 (128 bits = 16 bytes) juntas en un espacio total de palabra de 32 bytes.

Verifica la salida de Remix IDE:

El valor d = 40 en decimal (28 en hexadecimal) se almacena en el primer byte de la palabra, seguido de c = 30 en decimal (1e en hexadecimal).

Observa que el primer elemento almacenado en un slot de almacenamiento agrupado se alinea en la parte inferior.

Una pequeña tarea para hacer en casa:

Verifica la salida en el Remix IDE cuando reorganices el orden en el código de arriba de la siguiente manera:

uint256 a = 10;
   uint256 b;
   uint128 d = 40;
   uint128 c = 30;
   uint256 e = 50;
Enter fullscreen mode Exit fullscreen mode

Llegando al punto en el que proporcioné un argumento de cadena "Manu" como entrada en la función storeString:

El toma ese argumento en la memoria (memoryName) y lo almacena/escribe en el almacenamiento (storageName), y así es como obtienes el valor "Manu" en el slot # 4.

Observa detenidamente todas las salidas anteriores y notarás que la EVM trata todos los datos internamente como palabras de bytes32.

Con esto concluyo la Parte 1.

A continuación, explicaré el concepto y el uso de algo llamado** Punteros de Almacenamiento/Referencia**.

Y, como siempre, agradecería mucho si pudieras proporcionar un feedback/reacción honesta y/o crítica constructiva sobre este texto para que pueda mejorarlo.

Hasta la próxima, ¡buena programación (en Solidity)! :)

Artículo original publicado por Manu Kapoor. Traducido por Juliana Cabeza.

Discussion (0)