Using modifiers for permission access control

In our contracts we have 3 permission access levels.
Admin, operator, Alerter. with designated modifiers. namely: OnlyAdmin, OnlyOperator, OnlyAlerter. All in one base contract inherited by many others.

in our new version. We have reached bytecode size limit. And from the quest to reduce code size, decided to switch those modifiers into functions with same name and logic. It achieved up to 1000 bytes code size savings in one contract.

I know onlyOwner by zeppelin is also a modifier.
but couldn’t find anywhere why is it important for the access permission to be handled in a modifier rather then a function.
I know its inlined so gas consumption is a bit better. But for us the relevant functions are not used frequently.

But either then that - what is hiding their?

thanks

1 Like

Hi @ilanD,

My understanding is that modifiers are the most appropriate way to handle checks, especially when being used in multiple places.

Ideally we should be using the semantics of the language and not have to change the way we write our code to suit the current implementation of the compiler (which can change).
That being said, you could write a require statement using the owner() getter, or even put that check in a function.

There is an open issue on Solidity talking about the way modifiers are handled: https://github.com/ethereum/solidity/issues/6584

Thanks for the quick reply.

you write:

Could you elaborate more on this?
what is the reason behind it?

From what I see, when using internal functions we avoid code duplication and achieve similar readability.

so what else is hiding there?

1 Like

Hi @ilanD,

I am not sure what else to add about function modifiers, that is my understanding of how Solidity should be used. (apologies if I am missing something)

From the Solidity documentation: https://solidity.readthedocs.io/en/v0.6.7/contracts.html#function-modifiers

Modifiers can be used to change the behaviour of functions in a declarative way. For example, you can use a modifier to automatically check a condition prior to executing the function.


That function modifiers are inlined is the current implementation of the compiler, which could change. (e.g. https://github.com/ethereum/solidity/issues/6584)

In your situation where you are hitting the code size limit for a contract right now then understandably you need to work around this and you are doing that using functions.

My point was, in general and ideally, we shouldn't have to change how we use the language, the compiler should automagically handle this for us and optimize for contract byte size or gas cost depending how we configure it. Rather than we having to understand the internal workings of the compiler and manually optimize to handle the implementation.

Let me know if you need more information.

I think you are doing what you need to do to handle the code size limitation. Ideally you would be able to get the compiler to do this for you.

OK

I totally agree with the idea that it is better to use language components as was intended by the language creators.

but no concrete reasons are given to explain why modifiers would work better for condition checking.

I agree that its better if the compiler can solve my code size issues.

but considering the size limit which is very tight. Many times it is easier for the developer to optimize each file part for its intended usage, rather then “explaining” the compiler how each part of the code should be optimized.

in our case we have around 10 - 12 external functions. Each triggering a different code path.
Most are setters getters which don’t require gas optimization.
only one of them should be optimized for low gas consumption. others should prioritize smaller bytecode size.

1 Like

As you said modifiers are inlined, the drawback to this as you have said, is that you increase the bytecode size of your contract. An alternative is swapping your modifiers for internal functions, which in turn will get called like a normal function instead of being inlined which comes at slight increase in run-time cost.

Overall, I agree with the sentiment that modifers are the way to go in many cases - specially so for simple checks like onlyOwner. That said, I also think we can fall in the trap of abusing modifiers and endup with ridiculous function declarations that don’t really add any clarity to the code, in this case without having some code to see it’s hard to tell if this is the case or if it’s just “bad luck” that you ended-up with such a high bytecode size.

That said, I don’t think modifiers are just the “best” way to approach checks, as you can always use a require statement and an internal view function, but they are definitely something to consider when having repetitive checks regarding function calls (like onlyOwner).

All in all, IMO there is no “best” answer here and I don’t think anyone will say “you suck at Solidity” because you changed a bunch of modifiers into internal functions for the sake of keeping your bytecode size small You can also consider checking other factors for bytecode size, like looking for repetitive checks, revert reason strings that are too long (i.e they don’t fit in 32 bytes) or even considering using a library for some of the code (altough that comes with other security risks).

1 Like

I believe function modifiers are the most appropriate way in the Solidity language for checking permissions based on the Solidity documentation, unless you run into other constraints. I don't have any other reasons beyond that.

Function modifiers in the current implementation of the compiler are inlined, and have lower gas cost but when used in multiple locations have a higher code size.

@ilanD your approach is fine given the code size constraints and the apparent optimizations currently available in the compiler.


@madness thank you for joining the discussion :pray: It is greatly appreciated.

For me, the ideal is that we can write code in a readable, testable and auditable manner and the compiler optimizes for gas and code size. We shouldn't have to change how we write code to suit the implementation of the compiler.

2 Likes

For me, the ideal is that we can write code in a readable, testable and auditable manner and the compiler optimizes for gas and code size. We shouldn’t have to change how we write code to suit the implementation of the compiler.

Couldn't agree more with this! That said, solc is a high-maintenance cheeky rascal that seems to enjoy making things a little bit harder for us mortals, hopefully in the near future all these little problems will be fixed and it will make edge cases like this one dissapear and our lifes a little bit easier hahaha.

1 Like