La ABI de un contrato de Ethereum es una descripción de su interfaz que ayuda a las aplicaciones a codificar llamadas a funciones e interpretar los datos devueltos. ABI significa Application Binary Interface, o interfaz binaria de aplicación. El archivo JSON recoge información como los nombres de las funciones, los tipos de sus parámetros y los tipos de retorno. Describe la interfaz de forma estructurada; evaluar la implementación requiere pruebas adicionales.
Conviene distinguir qué declara una interfaz, cómo se representan sus datos y qué hace realmente el programa ejecutable. Entender esas funciones permite leer una ABI sin considerar que los nombres conocidos o los resultados legibles demuestran seguridad.
ABI, código fuente y bytecode cumplen funciones diferentes
La documentación de Ethereum sobre compilación describe la ABI JSON como una descripción del contrato desplegado y sus funciones. También explica que la máquina virtual de Ethereum, o EVM, necesita bytecode para ejecutar un contrato.
El código fuente interviene antes de la ejecución. En el ejemplo Multiply7 de la documentación de JSON-RPC de Ethereum, un contrato escrito en Solidity se compila a bytecode para la EVM. Ese ejemplo ilustra una relación de compilación; no verifica el código fuente de ningún otro despliegue.
A partir de estas premisas, podemos distinguir tres representaciones:
- Código fuente: el programa que se proporciona para su compilación en el ejemplo.
- Bytecode: la representación que ejecuta la EVM.
- ABI JSON: la descripción de la interfaz utilizada para interpretar funciones y sus datos tipados.
Esta distinción se deduce de sus funciones documentadas. Disponer de una representación no establece, por sí solo, su correspondencia con las otras dos. En particular, encontrar un archivo ABI no acredita que corresponda al despliegue que se está examinando.
Qué indica la entrada de una función
La especificación de la ABI de Solidity define los campos de la interfaz JSON. Para una función con nombre, empieza por tres elementos:
name: el nombre de la función.inputs: una descripción ordenada de sus parámetros, con el nombre y el tipo canónico de cada uno.outputs: una descripción de los parámetros de retorno mediante objetos similares a los deinputs.
Los parámetros de tipo tupla también utilizan components para describir los campos que los componen. El array de salidas puede estar vacío. Estos detalles importan porque la descripción debe conservar la estructura, además de enumerar palabras reconocibles.
No des por hecho que todas las entradas de una ABI siguen el patrón de una función con nombre. Las entradas de constructor, receive y fallback carecen de nombre y de salidas; receive y fallback tampoco tienen entradas. Los campos esperados dependen de la clase de entrada examinada.
Una salida declarada explica la estructura de retorno prevista. No acredita que se haya producido una ejecución ni que esta haya devuelto un valor concreto. Mantén separadas la declaración y las pruebas de un resultado real.
Cómo se relacionan los nombres y los tipos con el selector
En una llamada ordinaria a una función mediante ABI, el selector se obtiene de la firma canónica de la función. Esta combina su nombre con los tipos de sus parámetros entre paréntesis. El selector son los primeros cuatro bytes del hash Keccak-256 de esa firma.
Por eso, reconocer un nombre solo resuelve parte de la interpretación. Los tipos de los parámetros también forman parte de la firma canónica. Comparar únicamente el nombre deja fuera información utilizada para obtener el selector.
Los tipos de retorno cumplen otra función: quedan excluidos de la firma utilizada para obtener el selector, aunque la descripción JSON completa incluya las salidas. De esa separación documentada se deduce que el selector, por sí solo, no proporciona el esquema de retorno.
Esta relación explica cómo interpretar la interfaz. No establece que un selector identifique una implementación segura ni debe utilizarse como un dictamen de seguridad.
Por qué la codificación y la descodificación necesitan un esquema
La codificación ABI depende de los tipos de los valores. La especificación indica expresamente que la codificación no es autodescriptiva: hace falta un esquema para descodificarla. Aquí, el esquema aporta la estructura y los tipos necesarios para interpretar los datos.
Esto plantea dos tareas distintas. Primero, identificar los tipos y la estructura declarados. Después, establecer por qué esa descripción corresponde a los datos examinados. Una ABI resuelve la primera tarea en la medida en que contenga la descripción pertinente; su correspondencia con un despliegue sigue siendo una cuestión aparte.
Los valores de retorno de una función se agrupan en una tupla y se codifican conjuntamente conforme a la ABI. Por tanto, la descripción de las salidas importa al interpretar los datos devueltos, aunque sus tipos no intervengan en la obtención del selector.
Una interpretación legible debe seguir condicionada a que el esquema sea adecuado. La deducción tiene un límite preciso: como la descodificación depende de una descripción proporcionada, interpretar los bytes con ella no verifica de manera independiente la implementación ejecutable.
Un ejercicio hipotético de lectura de interfaz
Imagina un contrato ficticio para una clase con una función llamada lookupRecord. Su ABI hipotética declara una entrada denominada recordId, de tipo uint256, y dos salidas llamadas found y label, de tipos bool y string. Es un ejemplo inventado, no un contrato desplegado ni una instrucción para interactuar con uno.
La interfaz permite identificar el nombre de la función, el tipo de entrada y la estructura de retorno de dos elementos. Su firma canónica sería lookupRecord(uint256). Los tipos de salida declarados no aparecerían en la firma utilizada para obtener el selector.
Supongamos ahora que la ficha del ejercicio solo proporciona esta interfaz y pregunta qué etiqueta devuelve la función para el registro 7. La respuesta respaldada por los datos es que el resultado no está establecido. La declaración aporta un esquema de retorno, pero el ejercicio no proporciona el comportamiento de la implementación ni un resultado de ejecución.
Del mismo modo, el nombre no basta para concluir que la función consulta una base de datos determinada, aplica una regla concreta o produce información exacta. Es una deducción sobre las pruebas limitadas del ejercicio. Harían falta pruebas adicionales sobre la implementación y los resultados para respaldar esas conclusiones.
Lista de comprobación para interpretar una ABI
- Identifica el archivo: distingue la interfaz JSON del código fuente y del bytecode ejecutable.
- Comprueba la correspondencia: busca qué pruebas vinculan la ABI proporcionada con el despliegue examinado.
- Lee la clase de entrada: aplica los campos de las funciones con nombre donde corresponda.
- Conserva la estructura de entrada: anota nombres, tipos canónicos, orden y componentes de las tuplas cuando existan.
- Lee las salidas por separado: distingue la estructura de retorno declarada de un resultado observado.
- Precisa cómo se obtiene el selector: considera la firma canónica formada por el nombre y los tipos de entrada, excluyendo los tipos de retorno.
- Expón los supuestos de descodificación: identifica el esquema del que depende la interpretación.
- Limita la conclusión: interpretar la interfaz no demuestra por sí solo la seguridad de la implementación.
Ese último límite se deduce de la función descriptiva de la ABI, de la dependencia de un esquema para descodificar y del papel ejecutable del bytecode. No es una conclusión de que un contrato concreto sea inseguro. Los enlaces documentales fundamentan esta explicación; no certifican un despliegue.
Para ampliar la distinción entre las pruebas disponibles y las conclusiones que respaldan, consulta qué demuestra la verificación de una transacción en Ethereum.
Material educativo elaborado con ayuda de inteligencia artificial. El ejercicio hipotético sirve para comprender interfaces; no recomienda enviar una transacción ni firmar una solicitud de una cartera.