Cuando Solidity genera el bytecode para el smart contract que se va a desplegar, añade metadatos sobre la compilación al final del bytecode. Examinaremos los datos contenidos en este bytecode.
Un smart contract simple
Veamos la salida del compilador del smart contract de Solidity más simple posible
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract Empty {
constructor() payable {}
}
El contrato literalmente no hace nada. Podemos compilarlo para ver el init code con solc --optimize-runs 1000 --bin C.sol. Obtenemos la siguiente salida
======= C.sol:Empty =======
Binary:
6080604052603e80600f5f395ff3fe60806040525f80fdfea26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c63430008140033
Eso parece terriblemente grande para un contrato que no hace nada, ¿verdad? Entendamos qué es todo este bytecode.
Cuando compilamos el código con solc --optimize-runs 1000 --bin --no-cbor-metadata C.sol obtenemos la siguiente salida:
======= C.sol:Empty =======
Binary:
6080604052600880600f5f395ff3fe60806040525f80fd
¡Eso es mucho más pequeño! Entonces, ¿qué es toda esa información adicional?
Metadatos de Solidity
Por defecto, el compilador de Solidity añade metadatos al final del init code “real”, el cual se almacena en la blockchain cuando el constructor termina de ejecutarse. A continuación se muestra el código “extra”:
fea26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c63430008140033
Los últimos dos bytes 0033 significan “mira hacia atrás 0x33 bytes, esos son los metadatos”. Esto se refiere a todo el código entre el fe inicial (que es el opcode INVALID) y el 0033 final. Podemos comprobar que efectivamente se trata de 0x33 bytes.
# fe and 0033 are not included
>>>hex(len('a26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c6343000814') // 2)
# '0x33'
Entonces, ¿qué es esta cadena de 0x33 (51 en decimal)?
Podemos obtener una pista si hacemos un cambio pequeño y aparentemente inconsecuente en el código fuente. El cambio es literalmente solo un comentario adicional.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract Empty {
// nothing
constructor() payable {}
}
La siguiente captura de pantalla muestra un antes y un después.

Puedes ver que las secciones subrayadas han cambiado a pesar de que la funcionalidad del código no ha cambiado. Explicaremos el código de los recuadros en la siguiente sección.
Decodificando los metadatos
Al principio, parecerá que mágicamente estamos sacando subcadenas de la nada; ten paciencia con nosotros.
Veamos el hexadecimal en el recuadro azul de arriba
>>> bytes.fromhex("69706673").decode("ASCII")
'ipfs'
A continuación, veamos el código en el recuadro rojo
>>> bytes.fromhex("736f6c63").decode("ASCII")
'solc'
Eso nos da una pista sobre lo que contienen estos datos: un hash de IPFS y la versión del compilador de Solidity.
El Hash de IPFS
La sección subrayada en amarillo, junto con el recuadro turquesa, se puede poner en el siguiente script de python (ten en cuenta que estamos usando la versión del código con el comentario // nothing)
import base58
hex_ipfs_hash = "12206a68b6b8bcc01ba559ec3adf7a387b6c4210a5dc69a05d038e9d17cae3fa373b"
bytes_str = bytes.fromhex(hex_ipfs_hash)
print(base58.b58encode(bytes_str).decode("utf-8"))
# QmVW2XyafSxDtiSqirJRauuT5SaQtGnQYsxxyYHrFmRTEa
El Qm...RTEa es el hash de IPFS del archivo de metadatos producido por el compilador. Esta sección de código (turquesa y amarillo) está codificada de manera diferente a los recuadros anteriores. Específicamente, el hash de IPFS (el turquesa y amarillo) es una versión codificada en base58 de los datos hexadecimales “1220…RTEa”.
Este es el hash de IPFS que obtendrías si subieras el archivo JSON del compilador de Solidity a IPFS. Aquí está el archivo JSON en cuestión.

Podemos guardar el archivo JSON como un archivo real y luego validar que el hash coincida con el que produjimos en python anteriormente. Necesitarás tener instalada la herramienta de línea de comandos de ipfs (cómo instalar).
mkdir out
solc --optimize-runs 1000 --bin --metadata C.sol --output-dir out
# Compiler run successful. Artifact(s) can be found in directory "out".
ipfs add -qr --only-hash out/Empty_meta.json
# QmVW2XyafSxDtiSqirJRauuT5SaQtGnQYsxxyYHrFmRTEa
Esto coincide con el hash de antes.
¿No causará esto colisiones de hash?
Si dos contratos con código fuente y configuraciones de compilador idénticos almacenan su código fuente verificado en IPFS, los hashes de IPFS colisionarán, pero esto es deseable porque en realidad ahorra espacio de almacenamiento. Los smart contracts se identifican de manera única mediante la combinación del chain id y su dirección, no por el contenido de IPFS.
Obteniendo la versión de Solidity
Finalmente, si convertimos la sección en el recuadro naranja, vemos la versión de Solidity.
>>> 0x00 # solidity is version 0
0
>>> 0x08 # major version
8
>>> 0x14 # minor version
20
# correct, we used solidity 0.8.20
¿Por qué existen los metadatos de los smart contracts?
Estos metadatos añaden 53 bytes adicionales al coste de despliegue, lo cual se traduce en 10.600 de gas extra (200 por bytecode) + el coste de calldata (16 de gas por bytes distintos de cero, 4 de gas por byte cero). Esto se traduce en hasta 848 de gas adicional en coste de calldata.
Entonces, ¿por qué incluirlo?
Esto permite que el código de los smart contracts sea verificado rigurosamente. El JSON de metadatos que emite el compilador incluye un hash del código fuente. Por lo tanto, si el código fuente cambia un poco, el archivo JSON de metadatos cambiará y su hash de IPFS cambiará.
Un truco curioso para reducir el gas mediante el hash de IPFS
Una forma obvia de optimizar el coste de gas en el momento del despliegue es usar la opción --no-cbor-metadata. Pero si necesitas esto para la verificación del contrato, todavía puedes reducir el coste de gas minando hashes de IPFS que contengan una gran cantidad de bytes cero. Cuando se despliega el contrato, los bytes cero reducirán el coste de la calldata. Dado que el código fuente se convierte en un hash, comentarios incluidos, esto significa que uno puede minar comentarios que conduzcan a hashes de IPFS eficientes en gas que se añadirán al contrato. Ten en cuenta que esto significa que buscamos que la representación hexadecimal del hash tenga ceros, no la codificación base58.
Recursos adicionales
Puedes ver todas las opciones para manipular estos metadatos en la documentación de Solidity correspondiente. Sourcify proporciona una herramienta para analizar los metadatos de los smart contracts existentes.
Aprende más
Consulta nuestro Solidity Bootcamp para aprender temas más avanzados sobre smart contracts.
Publicado originalmente el 25 de mayo de 2023