1

Someone suggested to me to use a private function instead of a modifier because "private methods don't take up additional space when building the contract and because modifiers are like macros". So like in this example:

instead of

modifier onlyOwner {
    require(msg.sender == owner);
    _;
}

use

function onlyOwner() private {
    require(msg.sender == owner);
}

Is this true? Because I see in a lot of contracts, a modifier is usually used for something like restricting function to owner only. Also, is the modifier more expensive in gas cost compared to the private function?

1
  • Make sure to mark the correct answer. So people would know this has been answered Commented May 5, 2023 at 13:46

4 Answers 4

5

I read your question and also the current responses below and I believe your question hasn't really been answered yet.

Here is my response to your question

One of the reasons why someone might have suggested you to choose private functions over modifier is because:

Replacing modifiers with Private Function does actually help in Reducing the Contract Bytecode size

Why-here is a quick explanation 👇

  1. Modifiers are expanded inline:

    • When you use a modifier, Solidity copies the entire code of the modifier into every function that calls it.
    • In other terms, the Solidity compiler directly adds the modifier code to the function where the modifier is attached. This means that the modifier code becomes a part of the function and is included in the bytecode generated for that function.
    • This technically means that the modifier code is replicated in every function where the modifier is used, which can increase the size of the contract's bytecode.
  2. Private functions are only written once: Conversely, a private function is only written once and can be called from multiple places in your contract without increasing its size.

    • This makes it a much more efficient way to perform checks without duplicating code.

Example

  1. ModifierTest1st Contract Bytecode is = 2324 characters

    contract ModifierTest1st{
    address public owner;
    
    constructor(address _owner){
        owner = _owner;
    }
    
    modifier onlyOwner(){
        require(owner != address(0), "Invalid Owner");
        _;
    }
    
    function function_1() external onlyOwner{
    
    }
    
    function function_2() external onlyOwner{
    
    }
    
    function function_3() external onlyOwner{
    
    }
    

    }

  2. ModifierTest2nd Contract bytecode is only = 1804 characters

    contract ModifierTest2nd{
    address public owner;
    
    constructor(address _owner){
        owner = _owner;
    }
    
    function onlyOwnerPrivate() private{
        require(owner != address(0), "Invalid Owner");
    }
    
    function function_1() external{
         onlyOwnerPrivate();
    }
    
    function function_2() external {
        onlyOwnerPrivate();
    }
    
    function function_3() external {
        onlyOwnerPrivate();
    }
    

    }

Does this mean you should avoid using Modifiers?

Noooo, Modifiers are great. They adhere to best practices and are super helpful. It would help if you didn’t avoid them.

In fact, if your contract is fairly smaller and is quite below the maximum contract size threshold of 24.576 kb, modifiers shouldn’t be a problem.

However, considering a scenario where a check/validation is supposed to be used multiple times within your large contract and this unnecessarily increases your contract’s size, it's often a good idea to create a private function to perform the check. This can help reduce the contract's size and potentially improve its performance.

For such bulky contracts, it's completely fine to replace modifiers with private functions and save some space for any other imperative functions that you might want to add.

2
  • Great Answer. Thanks!
    – DevABDee
    Commented Nov 2, 2023 at 7:28
  • 🙏 glad to be of help
    – Zaryab
    Commented Dec 21, 2023 at 8:57
1

No, what you are doing there would not work. The placeholder is only syntax you can use with modifiers. Modifiers are mostly for readability. You could call internal functions that run the same code called at the start or end of your function instead. Don't think it affects gas in any way, but dont quote me on that.

1
  • I made a typo on inside the function. The placeholder should only be in the modifier. So my question remains. I should probably just try it myself on remix see if it saves gas.
    – Deche
    Commented Nov 10, 2022 at 17:09
1
modifier onlyOwner {
    require(msg.sender == owner);
    _;
}

The above onlyOwner is a modifier that can be applied to your functions to restrict their use to the owner. This modifier can be applied as many times as you want as appropriate for your contract design. The code for the function being modified is inserted where the _; is placed in the modifier.

function onlyOwner() private {
    require(msg.sender == owner);
    _;
}

The above onlyOwner() is actually a function, which is called onlyOwner and its visibility is private but the function cannot have the _; because that's only allowed inside a modifier. This function is not a modifier. Doing so forcibly will render a DeclarationError: Undeclared identifier for _;. This code block doesn't even run. Whether private methods take up additional space or not depends on your contract's design.

3
  • I made a typo on inside the function. The placeholder should only be in the modifier. The question is more like, is it true that the function version is better and saves more gas.
    – Deche
    Commented Nov 10, 2022 at 17:08
  • 2
    Not necessarily, I tested both in Remix separately and they both have the same transaction and execution cost (67066). However, the modifier version adheres more to best practices and can be inherited and overridden by derived contracts as needed. Commented Nov 10, 2022 at 17:49
  • Yeah I agree with you, better use the modifier version since it adheres to best practices. Thanks for clarifying that up!
    – Deche
    Commented Nov 13, 2022 at 17:47
0

Adding to Zaryab's answer: You can actually use both in one and have the benefits of both.

This means you can call the private function from your modifier. Additional you should use custom errors as they are generally more gas efficient than require().

contract Owner {
    address public owner;

    error NotOwner(address sender);

    constructor (address _owner) {
        owner = _owner;
    }

    modifier onlyOwner() {
        checkOnlyOwner();
        _;
    }

    function checkOnlyOwner() internal view {
        if (msg.sender != owner) {
            revert NotOwner(msg.sender);
        }
    }

    function doSomethingAsOwner() public onlyOwner() {
        // ...
    }
}

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.