2

I understand that in Solidity if a function has a modifier, this modifier runs first, prior the the logic within the function running. This is done for checks of ownership, checks of state, etc.

In Python, the concept of decorators seems very similar—a decorator replaces the decorated function with a new function that accepts the same arguments and returns whatever the decorated function was supposed to return, while also doing some extra processing.

In terms of what goes on behind the scenes, I know that in Python, the following are equivalent:

@clock
def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)

and

def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)
factorial = clock(factorial)

From my understanding, the following are equivalent in Solidity:

function startSale() notCanceled {
...
}

and

function startSale() {
    notCanceled()
    ...
}

My question is, are these two features meant to perform roughly the same task? When learning solidity, is comparing a decorator to a modifier a fair comparison to make? If so, why? If not, why not?

1 Answer 1

4

My question is, are these two features meant to perform roughly the same task? When learning solidity, is comparing a decorator to a modifier a fair comparison to make?

Yes, "wrapping" the function is a good way to think about it. It's actually closer to a decorator than the Solidity example in the question.

An example lock in both languages follows...

With a Python decorator:

def no_reentrancy(to_wrap):
  def wrapped(self, *args, **kwargs):
    if self._locked is True:
      raise Exception(...)
    self._locked = True

    return_val = to_wrap(self, *args, **kwargs)

    self._locked = False
    return return_val
  return wrapped

class MyClass:
  @no_reentrancy
  def sensitive(self, ...):
    ...

With a Solidity function modifier:

contract Mutex {
    bool locked;
    modifier noReentrancy() {
        require(
            !locked,
            "Reentrant call."
        );
        locked = true;
        _;
        locked = false;
    }

    /// This function is protected by a mutex, which means that
    /// reentrant calls from within `msg.sender.call` cannot call `f` again.
    /// The `return 7` statement assigns 7 to the return value but still
    /// executes the statement `locked = false` in the modifier.
    function f() public noReentrancy returns (uint) {
        require(msg.sender.call());
        return 7;
    }
}
2
  • 1
    That makes great sense. Thank you for the quick response. As an aside, what do you recommend is the best way to really learn and understand decorators? I understand how to implement the built-ins, as well as what they do, but when it comes to making a custom decorator, I find it difficult to do. Do you have good resources for this?
    – user45378
    Aug 29, 2018 at 19:13
  • What if you have multiple modifiers? what is the precedence? and does the underscore stand for the pure function or the function + all the modifiers of higher precedence?
    – J3STER
    Nov 12, 2022 at 18:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.