---
title: "¿Cómo funcionan las llamadas a contratos en Yul?"
description: "Aprende cómo funcionan las llamadas a contratos dentro del lenguaje de programación de smart contracts Yul"
---

# ¿Cómo funcionan las llamadas a contratos en Yul?

[Yul](http://www.alchemy.com/overviews/what-is-yul) es un lenguaje de programación intermedio que se puede usar para escribir una forma de lenguaje ensamblador dentro de [smart contracts](https://www.alchemy.com/overviews/solidity-smart-contract). Ahora que entiendes la sintaxis, el storage y cómo funciona la memoria en Yul, es momento de llamar contratos.

## **¿Cómo funcionan las llamadas a contratos en yul?**

En la sección final de esta serie veremos cómo funcionan las llamadas a contratos en Yul. Antes de ver algunos ejemplos, necesitamos aprender algunas operaciones más de Yul. Veamos.‍

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Instruction", dataType: "object" },
      { key: "2", width: 200, title: "Explanation", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>gas()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>La cantidad de gas que todavía está disponible para la ejecución</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>gasPrice()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El precio del gas de la transacción</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>address()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>La dirección del contrato actual</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>balance(a)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El balance de ether, en wei, de la dirección a</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>selfbalance()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Lo mismo que llamar a balance(address()), pero un poco más barato</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>caller()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>La dirección que llamó al contrato (msg.sender)</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>origin()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>La dirección que originó la transacción (tx.origin)</p>", tooltip: "", icon: "" },
        id: 6,
      },
      {
        "1": { title: "<p>callvalue()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>La cantidad de ether, en wei, que se envió en la llamada al contrato (msg.value)</p>", tooltip: "", icon: "" },
        id: 7,
      },
      {
        "1": { title: "<p>calldataload(p)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El calldata comenzando en la posición p (solo 32 bytes de datos)</p>", tooltip: "", icon: "" },
        id: 8,
      },
      {
        "1": { title: "<p>calldatacopy(t, f, s)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El calldata se copia a la ubicación de memoria t. Comenzando desde la posición f en calldata. Copia s bytes de datos.</p>", tooltip: "", icon: "" },
        id: 9,
      },
      {
        "1": { title: "<p>extcodesize(a)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El tamaño del código en la dirección a</p>", tooltip: "", icon: "" },
        id: 10,
      },
      {
        "1": { title: "<p>returndatasize()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El tamaño del último returndata</p>", tooltip: "", icon: "" },
        id: 11,
      },
      {
        "1": { title: "<p>returndatacopy(t, f, s)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Copia s bytes desde el return data en la posición f a la memoria en la posición t</p>", tooltip: "", icon: "" },
        id: 12,
      },
      {
        "1": { title: "<p>timestamp()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El timestamp actual del bloque en segundos</p>", tooltip: "", icon: "" },
        id: 13,
      },
      {
        "1": { title: "<p>number()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>El número de bloque actual</p>", tooltip: "", icon: "" },
        id: 14,
      },
      {
        "1": { title: "<p>call(g, a, v, in, insize, out, outsize)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Llama a un contrato en la dirección a, con gas g, pasando v wei como msg.value, pasando tx.data desde la ubicación de memoria in - insize, y almacenando el return data en la ubicación de memoria out - outsize. Retorna 1 si la llamada fue exitosa, de lo contrario retorna 0.</p>", tooltip: "", icon: "" },
        id: 15,
      },
      {
        "1": { title: "<p>delegatecall(g, a, in, insize, out, outsize)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Similar a call(). La principal diferencia es que delegatecall() actualiza las variables de estado en el contrato que hace la llamada. A menudo se usa para contratos proxy. Es importante notar que el storage layout debe ser idéntico al del contrato al que llamas, para evitar sobrescribir variables de storage no deseadas. Nota que falta el parámetro v. No puedes enviar ether con delegatecall().</p>", tooltip: "", icon: "" },
        id: 16,
      },
      {
        "1": { title: "<p>staticcall(g, a, in, insize, out, outsize)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Similar a call(). Excepto que no se puede usar para llamar a contratos que cambian el estado de la blockchain (es decir, funciones marcadas como pure y view). Nota que falta el parámetro v. No puedes enviar ether con staticcall().</p>", tooltip: "", icon: "" },
        id: 17,
      },
    ],
  }}
/>

Bien, ahora veamos algunos contratos nuevos para estos ejemplos. Primero, veamos el contrato que vamos a llamar.

Este contrato tiene dos variables de storage, var1 y var2, que se almacenan en los storage slots 1 y 2 respectivamente.

La función a\(\) requiere que el usuario envíe al menos 1 ether al contrato, de lo contrario revierte.

Luego, la función a\(\) actualiza var1 y var2 y las retorna.

La función b\(\) simplemente lee var1 y var2 y las retorna.

Antes de pasar a nuestro contrato que llama al contrato CallMe, necesitamos tomarnos un momento para entender los function selectors.

Veamos el siguiente calldata para una transacción `0x773d45e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002`.

Los primeros 4 bytes del calldata son lo que se conoce como el function selector \(0x773d45e0\). Así es como el EVM sabe qué función quieres llamar. El function selector se obtiene tomando los primeros 4 bytes del hash de un string con la firma de la función.

Entonces, la firma de la función a\(\) sería a\(uint256,uint256\).

Tomando el hash de este string obtenemos: `0x773d45e097aa76a22159880d254a5f1db8365bc2d0f0987a82bda7dfd3b9c8aa`.

Al ver los primeros 4 bytes, vemos que es igual a 0x773d45e0.

Nota la ausencia de espacios en la firma. Esto es importante porque agregar espacios nos daría un hash completamente distinto. No tienes que preocuparte por obtener los selectors para nuestros ejemplos de código, yo los proporcionaré.

Empecemos viendo el storage layout.

Nota cómo var1 y var2 tienen el mismo layout que el contrato CallMe. Recordarás que mencioné que el layout debe ser el mismo que el de nuestro otro contrato para que delegatecall\(\) funcione correctamente.

Cumplimos con esos requisitos y podemos tener otras variables \(selectorA y selectorB\) siempre que nuestras nuevas variables se agreguen al final. Esto evita colisiones de storage.

Ahora estamos listos para hacer nuestra primera llamada a un contrato.

### **Cómo usar staticcall\(\) en yul**

Empecemos con algo simple, staticcall\(\). Aquí está nuestra función:

**Esto es lo que está pasando:**

- Obtenemos el function selector de b\(\) desde storage cargando el slot 2 \(ambos selectors están empaquetados en un mismo slot\)
- Hacemos un shift a la derecha de 4 bytes \(32 bits\) para aislar selectorB
- Almacenamos el function selector en el scratch space de la memoria
- Hacemos nuestra static call
- Pasamos gas\(\) \(también puedes especificar la cantidad de gas\)
- Pasamos el parámetro \_callMe como la dirección del contrato
- Verificamos si la llamada a la función fue exitosa; de lo contrario, retornamos sin datos.
- Retornamos nuestros datos desde memoria y vemos los valores 1 y 2

**Nota:** 0x1c y 0x20 indican que queremos pasar los últimos 4 bytes de lo que almacenamos en el scratch space. Los últimos dos parámetros de staticcall\(\) especifican que queremos almacenar el return data en las ubicaciones de memoria 0x80 - 0xc0.

### **Cómo usar call\(\) en yul**

Ahora veamos call\(\). Vamos a llamar a la función a\(\) de CallMe. ¡Recuerda enviar al menos 1 ether al contrato! Voy a pasar 3 y 4 como \_var1 y \_var2 para este ejemplo.

**Aquí está el código:**

Bien, similar a nuestro ejemplo anterior, tenemos que cargar el slot2. Esta vez, sin embargo, vamos a hacer un mask de selectorB para aislar selectorA.

Ahora almacenaremos el selector en 0x80.

Como necesitamos parámetros del calldata, vamos a usar calldatacopy\(\). Le estamos indicando a calldatacopy\(\) que:

- almacene nuestro calldata en la ubicación de memoria 0xa0
- se salte los primeros 36 bytes
- almacene el tamaño del calldata menos 36 bytes

**Nota:** los primeros 4 bytes que saltamos son el function selector de callA\(\) y los siguientes 32 bytes son la dirección de callMe.

Ahora estamos listos para hacer nuestra llamada al contrato.

Como la última vez, pasamos gas\(\) y \_callMe. Sin embargo, esta vez pasamos nuestro calldata desde 0x9c \(los últimos 4 bytes de la serie de memoria en 0x80\) hasta 0xe0, y almacenamos nuestros datos en la ubicación de memoria 0x100 - 0x120.

De nuevo, verificamos si la llamada fue exitosa y retornamos nuestro output. Si revisamos el contrato CallMe, vemos que los valores se actualizaron correctamente a 3 y 4.

Para aclarar aún más lo que está pasando, aquí está el layout de memoria justo antes de retornar:

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Memory Location", dataType: "object" },
      { key: "2", width: 200, title: "Value Stored", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>0x00</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space (Vacío)</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>0x20</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space (Vacío)</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>0x40</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x80 (Nunca actualizamos el Free Memory Pointer, ya que todas las operaciones se hacen manualmente en Yul.)</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>0x60</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Vacío</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>0x80</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x0000000000000000000000000000000000000000000000000000000000773d45e0 (Function Selector)</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>0xa0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x0000000000000000000000000000000000000000000000000000000000000003 (_var1)</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>0xc0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x0000000000000000000000000000000000000000000000000000000000000004 (_var2)</p>", tooltip: "", icon: "" },
        id: 6,
      },
      {
        "1": { title: "<p>0xe0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Vacío</p>", tooltip: "", icon: "" },
        id: 7,
      },
      {
        "1": { title: "<p>0x100</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x0000000000000000000000000000000000000000000000000000000000000003 (Primer valor de retorno)</p>", tooltip: "", icon: "" },
        id: 8,
      },
      {
        "1": { title: "<p>0x120</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x0000000000000000000000000000000000000000000000000000000000000004 (Segundo valor de retorno)</p>", tooltip: "", icon: "" },
        id: 9,
      },
      {
        "1": { title: "<p>0x140</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Free Memory Pointer (Vacío)</p>", tooltip: "", icon: "" },
        id: 10,
      },
    ],
  }}
/>

### **Cómo usar delegate call en yul**

En nuestra última sección veremos delegatecall\(\). El código se verá casi idéntico a call\(\), con un solo cambio.

El único cambio que hicimos fue cambiar call\(\) por delegatecall\(\) y eliminar callvalue\(\).

No necesitamos un callvalue\(\), porque delegatecall ejecuta el código de CallMe dentro de su propio estado. Por lo tanto, el statement require\(\) en a\(\) está verificando si se envió ether a nuestro contrato Caller. Si revisamos var1 y var2 en CallMe, vemos que no hubo cambios. Sin embargo, var1 y var2 en nuestro contrato Caller se actualizaron correctamente.

¡Esto concluye nuestra sección sobre llamadas a contratos! A continuación, aprende cómo funcionan [Memory](http://www.alchemy.com/overviews/yul-memory), [Storage](http://www.alchemy.com/overviews/yul-storage) y [Smart Contract Calls](http://www.alchemy.com/overviews/yul-contract-calls) en Yul.
