Hey @wei3erHase, sorry for the late response, most of the team is right now at DevConnect Istanbul.
The execution order depends on the linearization order and not on the override(...)
order specified (i know, weird). So in this case, because the GovernorTimelockControl
is the last contract defined in the is
inheritance list, it's considered the most derived contract.
You can test it out easily in Foundry:
// src/Inheritance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {console2} from "forge-std/console2.sol";
contract Governor {
function foo() public virtual {
console2.log("Governor");
}
}
contract GovernorTimelockControl {
function foo() public virtual {
console2.log("GovernorTimelockControl");
}
}
contract Inheritance1 is GovernorTimelockControl, Governor {
function foo() public override(GovernorTimelockControl, Governor) {
super.foo();
}
}
contract Inheritance2 is GovernorTimelockControl, Governor {
function foo() public override(Governor, GovernorTimelockControl) {
super.foo();
}
}
contract Inheritance3 is Governor, GovernorTimelockControl {
function foo() public override(GovernorTimelockControl, Governor) {
super.foo();
}
}
contract Inheritance4 is Governor, GovernorTimelockControl {
function foo() public override(Governor, GovernorTimelockControl) {
super.foo();
}
}
test/Inheritance.t.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console2} from "forge-std/Test.sol";
import {Inheritance1, Inheritance2, Inheritance3, Inheritance4} from "../src/Inheritance.sol";
interface IInheritance {
function foo() external;
}
contract InheritanceTest is Test {
IInheritance public i_1;
IInheritance public i_2;
IInheritance public i_3;
IInheritance public i_4;
function setUp() public {
i_1 = IInheritance(address(new Inheritance1()));
i_2 = IInheritance(address(new Inheritance2()));
i_3 = IInheritance(address(new Inheritance3()));
i_4 = IInheritance(address(new Inheritance4()));
}
function test_inheritance() public {
console2.log("=== Case 1 ===");
i_1.foo();
console2.log("=== Case 2 ===");
i_2.foo();
console2.log("=== Case 3 ===");
i_3.foo();
console2.log("=== Case 4 ===");
i_4.foo();
}
}
The result of running forge test -vv
will be the following:
[PASS] test_inheritance() (gas: 28639)
Logs:
=== Case 1 ===
Governor
=== Case 2 ===
Governor
=== Case 3 ===
GovernorTimelockControl
=== Case 4 ===
GovernorTimelockControl
Shouldn't the override order be inverse if we want to keep GovernorTimelockControl behaviour? The docs doesn't explain this setup.
This is actually a good prompt for the documentation, we can consider adding further explanation about this.
Also, given that this Governor is gonna be set on Optimism, what's the best way to address voting delay and periods, since the 12s per block is not ensured in this chain?
I'll recommend checking out EIP-6372 for customizing a contract clock. It's built-in in Governor and you can override it using something like this:
function clock() public view override returns (uint48) {
return uint48(block.timestamp);
}
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() public view virtual override returns (string memory) {
return "mode=timestamp";
}
Hope it helps!