Decodificando variables dinámicas con Foundry

En el anterior tutorial vimos cómo decodificar el input data de las transacciones. Solo profundizamos en las variables estáticas, en esta ocasión veremos cómo extraer la información de las variables dinámicas.

En solidity tenemos las siguientes variables dinámicas:

  • bytes
  • string
  • arrays dinámicos []
  • arrays fijos [N]

Vamos a crear un contrato muy simple para poder descifrar el input data que contiene variables dinámicas.

pragma solidity ^0.8;

contract Ejemplo {
    function transferir(uint256[] memory ids, address to) external {

    }

    function transferir2(uint256[] memory ids, address to, string memory texto) external {
        
    }
}

Antes de nada vamos a instalar Foundry para usar sus utilidades y así facilitarnos la vida para extraer los datos.

Instalaremos y ejecutaremos Foundry con los siguientes comandos.

curl -L https://foundry.paradigm.xyz | bash
foundryup

Ahora que tenemos todo listo, vamos a enviar la siguiente transacción al contrato que hemos desplegado (para estas pruebas lo podéis desplegar usando Remix).

[1,2,3,4],0xb8ff877ed78ba520ece21b1de7843a8a57ca47cb

Si miramos el input de la transacción veremos el siguiente texto.

0x142fa2940000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004

Como ya sabemos de anteriores tutoriales, los primeros 4 bytes es el function signature. La utilidad cast nos puede ser útil para averiguar cuál es el function signature:

cast sig "transferir(uint256[],address)"

Podemos usar la utilidad cast de Foundry para que nos muestre de forma “bonita” el texto del input. Para ello ejecutamos el siguiente comando.

cast pretty-calldata 0x142fa2940000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004

Nos devolverá el siguiente resultado:

 Method: 142fa294
 ------------
 [000]: 0000000000000000000000000000000000000000000000000000000000000040
 [020]: 000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb
 [040]: 0000000000000000000000000000000000000000000000000000000000000004
 [060]: 0000000000000000000000000000000000000000000000000000000000000001
 [080]: 0000000000000000000000000000000000000000000000000000000000000002
 [0a0]: 0000000000000000000000000000000000000000000000000000000000000003
 [0c0]: 0000000000000000000000000000000000000000000000000000000000000004

La primera línea nos indica a partir de qué posición encontraremos el contenido del array uint256, el valor es 0x40, el cual convertido a decimal equivale a 64 bytes, o lo que es lo mismo 128 caracteres.
Podemos usar la utilidad cast de nuevo para hacer la conversión de hexadecimal a decimal.

cast --to-dec 0x40

En la respuesta “bonita” que nos ha devuelto la utilidad cast, sería a partir de la tecera línea ([060]).
La segunda línea tiene el valor del segundo parámetro del método al que hemos llamado, en este caso un address.
La tercera línea nos indicará el tamaño del array que ha sido enviado, en este caso, 4 elementos.
Por último, las siguientes cuatro líneas contienen el valor de cada uno de los elementos del array.

Os dejo aquí otro ejemplo de invocación del método, enviando un array con más elementos para que podáis probar de decodificar la información por vuestra cuenta.

[1,2,3,4,5,6,7,8],0xb8ff877ed78ba520ece21b1de7843a8a57ca47cb
0x142fa2940000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008

La variable dinámica string se decodifica de una manera un poco distinta. Pongamos el siguiente ejemplo, donde enviamos los siguientes parámetros a la función.

[0,1,2],0xb8ff877ed78ba520ece21b1de7843a8a57ca47cb,prueba

Como input de la transacción llega el siguiente texto.

0xd8f8eb660000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000067072756562610000000000000000000000000000000000000000000000000000

Ejectuamos cast de nuevo recibiendo esta repuesta:

cast pretty-calldata 0xd8f8eb660000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000067072756562610000000000000000000000000000000000000000000000000000
 Method: d8f8eb66
 ------------
 [000]: 0000000000000000000000000000000000000000000000000000000000000060
 [020]: 000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb
 [040]: 00000000000000000000000000000000000000000000000000000000000000e0
 [060]: 0000000000000000000000000000000000000000000000000000000000000003
 [080]: 0000000000000000000000000000000000000000000000000000000000000000
 [0a0]: 0000000000000000000000000000000000000000000000000000000000000001
 [0c0]: 0000000000000000000000000000000000000000000000000000000000000002
 [0e0]: 0000000000000000000000000000000000000000000000000000000000000006
 [100]: 7072756562610000000000000000000000000000000000000000000000000000

La penúltima línea nos indica la longitud en bytes del string que ha recibido la función. En nuestro caso son 6 bytes (12 caracteres).
Para saber la cantidad de bytes del string ejecutamos cast con el valor hexadecimal de la penúltima línea:

cast --to-dec 0x06

Los strings se representan de izquierda a derecha, por lo que seleccionaremos los primeros 6 bytes (12 caracteres) para extraer el string enviado a la función.

Finalmente, para ver el texto en ASCII ejecutaremos de nuevo cast:

cast --to-ascii 707275656261

Como podemos observar, 707275656261 equivale al texto prueba que enviamos a la función transferir2.

0000000000000000000000000000000000000000000000000000000000000060
000000000000000000000000b8ff877ed78ba520ece21b1de7843a8a57ca47cb
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000006 0x06 -> cast --to-dec 0x06 == 6 bytes (12 characters)
7072756562610000000000000000000000000000000000000000000000000000 707275656261 -> cast --to-ascii 707275656261 == prueba

Deja una respuesta

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