¿Cuál es el orden de ejecución de los modificadores de funciones múltiples? [duplicar]

No sé por qué nadie hace esta pregunta (hice mi tarea e hice un montón de búsquedas en Google para obtener una respuesta), pero me cuesta entender cómo funcionan realmente los modificadores de funciones.

Seguro que es trivial cuando solo usas un modificador, o si usas la _notación siempre al final. Pero me encuentro con un código que se parece a esto:

modifier modA {
  // verify something
  _;
  // verify something
  _;
}
modifier modB {
  // verify something
  _;
  // verify something
  _;
}
modifier modC {
  // verify something
  _;
  // verify something
  _;
}

function Fun() modA modB modC {
  // Do something
}

Entonces finalmente me doy cuenta de que no tenía idea de lo que estaba pasando debajo.

En el caso anterior, ¿cómo se supone que funciona? Mirar la documentación realmente no ayuda porque solo dice que los modificadores simplemente reemplazan _con el código de la función. Pero, ¿y si hay varios de estos?

Entonces, si vamos de modAa modB, ¿ _se reemplazan con el siguiente modificador? ¿Qué quieren decir con "ser reemplazado por la función original"?

Respuestas (2)

Creo que una buena manera de entender lo que está pasando es experimentar con

Creé este contrato en Remix para probar/probar algunas de mis suposiciones sobre lo que sucedía con los modificadores:


pragma solidity ^0.4.18;

contract modifierTest {
    uint public modState1;
    uint public modState2;
    uint public modState3;

    modifier modA() {
        modState1 = modState1 + 1;
        _;
    }

    modifier modB() {
        modState2 = modState2 + 1;
        _;
        modState2 = modState2 + 1;
        _;
    }

    function func() public modA modB {
        modState3 = modState3 + 1;
    }
}

Inicialmente, todas las variables de estado del contrato se establecen en 0 de forma predeterminada.

Después de ejecutar la función func y examinar las variables de estado, vemos el siguiente resultado:


modState1 - uint256: 1
modState2 - uint256: 2
modState3 - uint256: 2
  • Podemos ver que el modificador modA solo se ejecutó una vez.
  • Podemos ver que el modificador modB solo se ejecutó una vez.
  • Podemos ver que tener más de un _; es una sintaxis válida
  • Podemos ver que la función func fue llamada dos veces

De esto, podemos entender que el ; en el modificador *modA* fue reemplazado por el código del modificador *modB* y que los * ;*s en el cuerpo del modificador modB fueron reemplazados por el código de la función func .

Entonces, si pasamos de modA a modB, ¿se reemplaza el _ con el siguiente modificador? ¿Qué quieren decir con "ser reemplazado por la función original"?

Con suerte, este ejemplo demuestra cómo el _ se reemplaza por el siguiente modificador y que el _ del modificador final se reemplaza por el código de la función.

Es importante tener en cuenta que funcse llama dos veces, porque tiene múltiples _;dentro modB, y no porque haya múltiples modificadores.

Supongamos una variable de todo el contrato 'a' con un valor de '0' y una función F con un modificador M:

function F() M() {
   a = 1;
}

modifier M() {
   if (a == 1) throw;
   _
}

esto no tirará. Pero esta versión:

modifier M() {
   _
   if (a == 1) throw;
}

lanzará Piense en los modificadores como un reemplazo de código (precompilado en términos de C++) en lugar de "llamar a". Los modificadores no son funciones. Es exactamente como si la función F se viera así en cada caso, que si asume que 'a' tiene un valor de '0' para comenzar, tiene dos comportamientos diferentes:

function F() {
   if (a == 1) throw;
   a = 1;
}

y

function F() {
   a = 1;
   if (a == 1) throw;
}

Para resolverlo, no importa cuán complicado sea, simplemente copie el código modificador en la ubicación adecuada en la función que se está modificando. Puede que tenga que copiar muchos niveles de profundidad, pero eso es lo que está haciendo el precompilador.

Me sorprende ver dos _en tus ejemplos. No sabía que podías hacer eso. Me parece una muy mala práctica usar _dos veces en un modificador. Hace que el código sea tremendamente difícil de entender conceptualmente. Por lo menos lo hace por mí.

He visto un par de ejemplos en línea donde los modificadores tienen múltiples _s. Aquí hay uno: ethereum.stackexchange.com/a/5864/21958 - Además, creo que entiendo lo que dices, pero ¿podrías aclarar la respuesta para asegurarte? Como entendí, estás diciendo que la función F() no se ejecuta hasta que pasa por cada modificador M1(), M2(), M3()... uno por uno, haciendo que M1() reemplace F() primero, luego reemplace el resultado con M2(), y así sucesivamente. ¿Y solo cuando llega al último modificador ejecuta realmente la función?
Tome el cuerpo del código en F y reemplace la barra inferior de M con él. Luego tome el resultado si ese y literalmente se convierte en el cuerpo de F. Todo esto sucede durante el paso de compilación, no en tiempo de ejecución. ¿Eso ayuda?