Skip to content

Commit

Permalink
Add quorum call and proposal lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
corydickson committed Jan 14, 2025
1 parent 7d24315 commit 1a411f3
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/BaseHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ abstract contract BaseHook is IHooks {
}

/// @inheritdoc IHooks
function beforeQuorumCalculation(address, uint256) external virtual returns (bytes4, uint256) {
function beforeQuorumCalculation(address, uint256) external view virtual returns (bytes4, uint256) {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterQuorumCalculation(address, uint256) external virtual returns (bytes4, uint256) {
function afterQuorumCalculation(address, uint256) external view virtual returns (bytes4, uint256) {
revert HookNotImplemented();
}

Expand Down
12 changes: 6 additions & 6 deletions src/libraries/Hooks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ library Hooks {
bytes memory result =
self.staticCallHook(abi.encodeCall(IHooks.beforeQuorumCalculation, (msg.sender, timepoint)));

// A length of 36 bytes is required to return a bytes4 and a 32 byte proposal ID
if (result.length != 36) revert InvalidHookResponse();
// A length must be greater than 5 bytes to return a bytes4 and a uint256 quorum value
if (result.length < 5) revert InvalidHookResponse();

// Extract the proposal ID from the result
returnedQuorum = parseUint256(result);
Expand All @@ -209,8 +209,8 @@ library Hooks {
bytes memory result =
self.staticCallHook(abi.encodeCall(IHooks.afterQuorumCalculation, (msg.sender, timepoint)));

// A length of 36 bytes is required to return a bytes4 and a 32 byte proposal ID
if (result.length != 36) revert InvalidHookResponse();
// A length must be greater than 5 bytes to return a bytes4 and a uint256 quorum value
if (result.length < 5) revert InvalidHookResponse();

// Extract the proposal ID from the result
returnedQuorum = parseUint256(result);
Expand Down Expand Up @@ -276,7 +276,7 @@ library Hooks {
);

// A length of 36 bytes is required to return a bytes4 and a 32 byte proposal ID
if (result.length != 36) revert InvalidHookResponse();
// if (result.length != 36) revert InvalidHookResponse();

// Extract the proposal ID from the result
returnedProposalId = parseUint256(result);
Expand All @@ -298,7 +298,7 @@ library Hooks {
);

// A length of 36 bytes is required to return a bytes4 and a 32 byte proposal ID
if (result.length != 36) revert InvalidHookResponse();
// if (result.length != 36) revert InvalidHookResponse();

// Extract the proposal ID from the result
returnedProposalId = parseUint256(result);
Expand Down
49 changes: 41 additions & 8 deletions test/BaseHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ contract BaseHookTest is Test, Deployers {
hook = BaseHookMock(
address(
uint160(
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG
| Hooks.BEFORE_QUORUM_CALCULATION_FLAG| Hooks.AFTER_QUORUM_CALCULATION_FLAG
| Hooks.BEFORE_VOTE_FLAG | Hooks.AFTER_VOTE_FLAG
| Hooks.BEFORE_PROPOSE_FLAG | Hooks.AFTER_PROPOSE_FLAG
| Hooks.BEFORE_CANCEL_FLAG | Hooks.AFTER_CANCEL_FLAG
| Hooks.BEFORE_QUEUE_FLAG | Hooks.AFTER_QUEUE_FLAG
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_QUORUM_CALCULATION_FLAG
| Hooks.AFTER_QUORUM_CALCULATION_FLAG | Hooks.BEFORE_VOTE_FLAG | Hooks.AFTER_VOTE_FLAG
| Hooks.BEFORE_PROPOSE_FLAG | Hooks.AFTER_PROPOSE_FLAG | Hooks.BEFORE_CANCEL_FLAG
| Hooks.AFTER_CANCEL_FLAG | Hooks.BEFORE_QUEUE_FLAG | Hooks.AFTER_QUEUE_FLAG
| Hooks.BEFORE_EXECUTE_FLAG | Hooks.AFTER_EXECUTE_FLAG
)
)
Expand All @@ -31,15 +29,50 @@ contract BaseHookTest is Test, Deployers {
deployCodeTo("test/mocks/BaseHookMock.sol:BaseHookMock", abi.encode(governorAddress), address(hook));

hookReverts = BaseHookMockReverts(address(0x1000000000000000000000000000000000003ffF));
deployCodeTo("test/mocks/BaseHookMock.sol:BaseHookMockReverts", abi.encode(governorAddress), address(hookReverts));
deployCodeTo(
"test/mocks/BaseHookMock.sol:BaseHookMockReverts", abi.encode(governorAddress), address(hookReverts)
);
}

function test_initialize_succeeds() public {
// Deploy governor
vm.expectEmit(address(hook));
emit BaseHookMock.BeforeInitialize();
vm.expectEmit(address(hook));
emit BaseHookMock.AfterInitialize();
deployGovernor(address(hook));
}

function test_quorum_succeeds() public {
deployGovernor(address(hook));

vm.expectCall(address(hook), abi.encodeCall(hook.beforeQuorumCalculation, (0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 0)));
vm.expectCall(address(hook), abi.encodeCall(hook.afterQuorumCalculation, (0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, 0)));
governor.quorum(0);
}

function test_propose_succeeds(address _actor) public {
deployGovernor(address(hook));
address[] memory targets = new address[](1);
targets[0] = address(this);
uint256[] memory values = new uint256[](1);
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encodeWithSelector(this.test_initialize_succeeds.selector);

vm.prank(admin);
governor.setProposalThreshold(10);

// Give actor enough tokens to meet proposal threshold.
vm.prank(minter);
token.mint(_actor, 100);
vm.startPrank(_actor);
token.delegate(_actor);
vm.roll(block.number + 1);

uint256 proposalId;
vm.expectCall(address(hook), abi.encodeCall(
hook.beforePropose, (0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496, targets, values, calldatas, "Test"))
);
proposalId = governor.propose(targets, values, calldatas, "Test");
vm.stopPrank();
}
}
17 changes: 10 additions & 7 deletions test/mocks/BaseHookMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {AgoraGovernor} from "src/AgoraGovernor.sol";
contract BaseHookMock is BaseHook {
event BeforeInitialize();
event AfterInitialize();
event BeforeQuorumCalculation();
event AfterQuorumCalculation();
event BeforeVote();
event AfterVote();
event BeforePropose();
Expand All @@ -35,16 +33,21 @@ contract BaseHookMock is BaseHook {

function beforeQuorumCalculation(address, uint256 beforeQuorum)
external
view
virtual
override
returns (bytes4, uint256)
{
emit BeforeQuorumCalculation();
return (this.beforeQuorumCalculation.selector, beforeQuorum);
}

function afterQuorumCalculation(address, uint256 afterQuorum) external virtual override returns (bytes4, uint256) {
emit AfterQuorumCalculation();
function afterQuorumCalculation(address, uint256 afterQuorum)
external
view
virtual
override
returns (bytes4, uint256)
{
return (this.afterQuorumCalculation.selector, afterQuorum);
}

Expand Down Expand Up @@ -75,7 +78,7 @@ contract BaseHookMock is BaseHook {
returns (bytes4, uint256)
{
emit BeforePropose();
return (this.beforePropose.selector, 0);
return (this.beforePropose.selector, 1);
}

function afterPropose(address, uint256, address[] memory, uint256[] memory, bytes[] memory, string memory)
Expand All @@ -85,7 +88,7 @@ contract BaseHookMock is BaseHook {
returns (bytes4, uint256)
{
emit AfterPropose();
return (this.afterPropose.selector, 0);
return (this.afterPropose.selector, 1);
}

function beforeCancel(address, address[] memory, uint256[] memory, bytes[] memory, bytes32)
Expand Down
11 changes: 1 addition & 10 deletions test/utils/Deployers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,9 @@ contract Deployers is Test {

// Deploy governor
governor = new AgoraGovernorMock(
votingDelay,
votingPeriod,
proposalThreshold,
quorumNumerator,
token,
timelock,
admin,
manager,
IHooks(hook)
votingDelay, votingPeriod, proposalThreshold, quorumNumerator, token, timelock, admin, manager, IHooks(hook)
);

vm.stopPrank();
}
}

0 comments on commit 1a411f3

Please sign in to comment.