-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Some modifiers could use internal functions to reduce code size impact #4466
Comments
I didn't know internal functions improve code size. So, is the proposal to replace these modifiers with internal functions or use the internal functions inside the modifiers? We can implement such changes without issues if the proposal is to keep the modifiers, however, replacing them with internal functions would require more discussion as it arguably undermines DX. I think it'd be helpful to have a benchmark with a rough estimate of how much it would save for both creation and runtime. A foundry test may do the job for this. |
Use the internal function inside the modifiers. This is something we commonly use in most of our modifiers but for some reason not on the ones I mentioned. |
Ok, that makes sense as it doesn't change the interface. I'm still not sure about the gas savings. My conclusion is that the code size reduction is significant but the runtime costs added could be even worse if used multiple times. Here you have the Foundry tests I made: PoC Code// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
/// === Base ===
contract AccessControlStorage {
bytes32 public constant NOT_HELD_ROLE = 0x00;
bytes32 public constant HELD_ROLE = bytes32(uint256(0x01));
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) internal _roles;
constructor() {
_roles[HELD_ROLE].members[msg.sender] = true;
}
}
/// === Inlined Setup ===
contract Inlined is AccessControlStorage {
modifier onlyRole(bytes32 role) {
require(_roles[role].members[msg.sender]);
_;
}
}
contract ModifiedInlined is Inlined {
function fail() public onlyRole(NOT_HELD_ROLE) {}
function succeed() public onlyRole(HELD_ROLE) {}
}
/// === Internal Setup ===
contract Internal is AccessControlStorage {
modifier onlyRole(bytes32 role) {
_checkRole(role, msg.sender);
_;
}
function _checkRole(bytes32 role, address account) internal view virtual {
require(_roles[role].members[account]);
}
}
contract ModifiedInternal is Internal {
function fail() public onlyRole(NOT_HELD_ROLE) {}
function succeed() public onlyRole(HELD_ROLE) {}
}
/// === Tests ===
contract ModifiedInlinedTest is Test {
ModifiedInlined private _modified;
function setUp() public {
_modified = new ModifiedInlined();
}
function testReverts() public {
vm.expectRevert();
_modified.fail();
}
function testPasses() public {
_modified.succeed();
}
}
contract ModifiedInternalTest is Test {
ModifiedInternal private _modified;
function setUp() public {
_modified = new ModifiedInternal();
}
function testReverts() public {
vm.expectRevert();
_modified.fail();
}
function testPasses() public {
_modified.succeed();
}
} When running
So far, we're saving 10,812 units of gas by using internal functions and we're saving 104 and 108 units of gas per function call. That means we'll need around 100 calls to waste more gas than saved. Interestingly, the functions you shared are not expected to be used frequently imo so it should be fine. Also note Governor is already a big contract so we'll be interested in a code size reduction if it doesn't affect runtime costs. |
For anyone wanting to take the issue, the implementation should be as follows:
Labeling as a good first issue but bear in mind a PR might be delayed since this is not a priority at the moment and other members in the team might want to take a look. |
I would like to work on this issue. Meanwhile, If there is any update let me know. |
I would Like to take this issue |
Can I take on this issue? |
There is already a PR for that issue. Please don't create multiple ones. |
The following modifier could be added to many methods and its implementation is large enough to consider using internal functions:
onlyGovernance
inGovernor.sol
Furthermore, other modifiers with fewer lines within each code block may benefit from also using internal functions:
onlyProxy
andnotDelegated
inUUPSUpgradeable.sol
.onlyInitializing
inInitializable.sol.
The text was updated successfully, but these errors were encountered: