Decodificando el input data en las transacciones en la blockchain de Meter

En este tutorial vamos a aprender cómo interpretar los datos que se envían a una función de un contrato. También exploraremos cómo se puede verificar un contrato en la blockchain de Meter y las diferencias que hay entre un contrato verificado y no verificado.

Crearemos un contrato muy simple para estas pruebas.

pragma solidity ^0.8.0;

contract PruebaInputData {
    uint256 public a;
    address public b;

    function prueba1(uint256 _a, address _b) public {
        a = _a;
        b = _b;
    }
}

Usaremos Remix https://remix.ethereum.org/ para desplegar el contrato en la testnet de Meter.

Podemos ver el contrato desplegado en esta URL: https://scan-warringstakes.meter.io/address/0x72016b16dc79c8209e3af3e726c6fbdafc2121c5

En estos momentos el contrato no está verificado, por lo que el explorador de Meter no es capaz de decodificar los datos que le enviamos a las funciones.
Como methodID veremos un código hexadecimal de 4 bytes y el valor del input data estará sin decodificar.

inputData3

Para que el explorador de bloque de Meter pueda decodificar el input data debemos verificar el contrato. Para ello debemos compilar y generar el JSON con los metadatos necesarios para verificar el contrato.

En Remix podemos conseguir los datos que precisamos. Accedemos a la pestaña del compilador y hacemos click en “Compilation Details”.

inputData3 1

Para que el explorador de bloque de Meter pueda decodificar el input data debemos verificar el contrato. Para ello debemos compilar y generar el JSON con los metadatos necesarios para verificar el contrato.

En Remix podemos conseguir los datos que precisamos. Accedemos a la pestaña del compilador y hacemos click en “Compilation Details”.

inputData4 1

Hacemos click en el botón “Download” y guardamos el JSON generado en la misma carpeta donde está el contrato (InputData.sol). Creamos un zip con el fichero .sol y .json.

Accedemos al contrato dentro del explorador de bloques de Meter y hacemos click en “VERIFY”.

inputData1

Seleccionamos el fichero zip que hemos creado hace unos instantes y hacemos click en “Verify”.

inputData2

Si todo va bien veremos un mensaje como este indicando que el contrato ha sido verificado.

inputData5

Podemos ver claramente cómo el explorador de bloques trata el input data de forma diferente entre un contrato sin verificar y otro verificado.

Contrato sin verificar
Input Data:
0xb7e29630000000000000000000000000000000000000000000000000000000000000000100000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

Contrato verificado
https://scan-warringstakes.meter.io/tx/0xbedaf34bb8722b4497cd911724724a82957ebbe27444607c60fc8f23bd45d5cd
To: 0x72016b16dc79c8209e3af3e726c6fbdafc2121c5
Value: 0MTR
function prueba1(uint256 _a, address _b)
MethodID: 0xb7e29630
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

Haciendo click en “Decode” obtenemos:

{
“_a”: “1”,
“_b”: “0x02aeEE85dF882f76043F44b527a3a63b42a83d95”
}

Decodificando a mano el input data

Para decodificar el input data de una transacción necesitamos conocer el ABI del contrato. Podemos tratar al ABI como si fuera una receta donde nos indica el nombre de la función y los parámetros que recibe.
Cada input data tiene una longitud de 32 bytes (64 caracteres).
Si quisiéramos convertir un valor hexadecimal a uint, por ejemplo 0x00000000000000000000000000000005 (0x5), lo convertiríamos a decimal y el resultado sería 5. En el caso de los int, hay que tener en cuenta que para tratar los negativos debemos seguir el formato “complemento a dos” (https://es.wikipedia.org/wiki/Complemento_a_dos)

El primer paso es extraer el methodID, el 0x y los primeros 8 caracteres.
El resto lo dividiremos en bloques de 32 bytes, recordemos que son 64 caracteres.

El tipo de dato bytes32 empezará por la izquierda (Big Endian) el resto por la izquierda (Little Endian).

Tenemos como base este input data:
0xb7e29630000000000000000000000000000000000000000000000000000000000000000100000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

Extraemos el MethodID:
000000000000000000000000000000000000000000000000000000000000000100000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

Dividimos el string restante en bloques de 64 caracteres:
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

Como tenemos acceso al ABI sabemos que el primer bloque corresponde a un uint256 y el segundo a un address.

Podemos usar la librería ethers para extraer el valor del address, pasando como argumento los 40 caracteres empezando por la derecha:

let address = ethers.utils.getAddress('02aeee85df882f76043f44b527a3a63b42a83d95');

0x02aeEE85dF882f76043F44b527a3a63b42a83d95

Para extraer el uint256 podemos usar la siguiente función:

console.log(BigInt('0000000000000000000000000000000000000000000000000000000000000001').toString());

Si el contrato tiene una función fallback() podemos generar nosotros mismos el contenido de CALLDATA y llamarlo directamente:
0xb7e29630000000000000000000000000000000000000000000000000000000000000000200000000000000000000000002aeee85df882f76043f44b527a3a63b42a83d95

inputData6

Para añadir la función fallback tan solo debemos añadir este código en el contrato.

fallback() external payable {

}

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *