Skip to content

Commit

Permalink
feat: proposal guardian expires
Browse files Browse the repository at this point in the history
  • Loading branch information
arr00 committed Aug 8, 2024
1 parent 8340937 commit 05503a7
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ cache
artifacts
yarn-error.log
.DS_Store
yarn.lock
35 changes: 29 additions & 6 deletions contracts/GovernorBravoDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ contract GovernorBravoDelegate is
bytes32 r,
bytes32 s
) public returns (uint) {
require(proposalId == proposalCount + 1, "GovernorBravo::proposeBySig: invalid proposal id");
require(
proposalId == proposalCount + 1,
"GovernorBravo::proposeBySig: invalid proposal id"
);
address signatory;
{
bytes32 domainSeparator = keccak256(
Expand Down Expand Up @@ -380,7 +383,9 @@ contract GovernorBravoDelegate is
Proposal storage proposal = proposals[proposalId];

// Proposer or proposalGuardian can cancel
if (msg.sender != proposal.proposer && msg.sender != proposalGuardian) {
if (
msg.sender != proposal.proposer && msg.sender != proposalGuardian()
) {
require(
(comp.getPriorVotes(proposal.proposer, block.number - 1) <
proposalThreshold),
Expand Down Expand Up @@ -658,6 +663,17 @@ contract GovernorBravoDelegate is
return (whitelistAccountExpirations[account] > block.timestamp);
}

/**
* @notice View function which returns the current proposal guardian
* @return The address of the current proposal guardian. If the proposal guardian expiry has passed, address(0) is returned.
*/
function proposalGuardian() public view returns (address) {
return
_proposalGuardianExpiry >= block.timestamp
? _proposalGuardian
: address(0);
}

/**
* @notice Admin function for setting the voting delay
* @param newVotingDelay new voting delay, in blocks
Expand Down Expand Up @@ -755,16 +771,23 @@ contract GovernorBravoDelegate is
/**
* @notice Admin function for setting the proposalGuardian. ProposalGuardian can cancel proposals
* @param account Account to set proposalGuardian to (0x0 to remove proposalGuardian)
* @param expiry Timestamp at which the set proposal guardian will expire
*/
function _setProposalGuardian(address account) external {
function _setProposalGuardian(address account, uint96 expiry) external {
require(
msg.sender == admin,
"GovernorBravo::_setProposalGuardian: admin only"
);
address oldProposalGuardian = proposalGuardian;
proposalGuardian = account;

emit ProposalGuardianSet(oldProposalGuardian, proposalGuardian);
emit ProposalGuardianSet(
_proposalGuardian,
_proposalGuardianExpiry,
account,
expiry
);

_proposalGuardian = account;
_proposalGuardianExpiry = expiry;
}

/**
Expand Down
15 changes: 9 additions & 6 deletions contracts/GovernorBravoInterfaces.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;


contract GovernorBravoEvents {
/// @notice An event emitted when a new proposal is created
event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description);
Expand Down Expand Up @@ -50,7 +49,11 @@ contract GovernorBravoEvents {
event WhitelistGuardianSet(address oldGuardian, address newGuardian);

/// @notice Emitted when the proposalGuardian is set
event ProposalGuardianSet(address oldProposalGuardian, address newProposalGuardian);
event ProposalGuardianSet(
address oldProposalGuardian,
uint96 oldProposalGuardianExpiry,
address newProposalGuardian,
uint newProposalGuardianExpiry);
}

contract GovernorBravoDelegatorStorage {
Expand All @@ -64,15 +67,13 @@ contract GovernorBravoDelegatorStorage {
address public implementation;
}


/**
* @title Storage for Governor Bravo Delegate
* @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new
* contract which implements GovernorBravoDelegateStorageV1 and following the naming convention
* GovernorBravoDelegateStorageVX.
*/
contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {

/// @notice The delay before voting on a proposal may take place, once proposed, in blocks
uint public votingDelay;

Expand Down Expand Up @@ -100,7 +101,6 @@ contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
/// @notice The latest proposal for each proposer
mapping (address => uint) public latestProposalIds;


struct Proposal {
/// @notice Unique id for looking up a proposal
uint id;
Expand Down Expand Up @@ -183,7 +183,10 @@ contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 {

contract GovernorBravoDelegateStorageV3 is GovernorBravoDelegateStorageV2 {
/// @notice Address which has the ability to cancel proposals
address public proposalGuardian;
address internal _proposalGuardian;

/// @notice Timestamp at which the proposalGuardian stored in the `_proposalGuardian` variable is set to expire
uint96 internal _proposalGuardianExpiry;
}

interface TimelockInterface {
Expand Down
12 changes: 10 additions & 2 deletions test/ForkTestSimulateUpgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
reset,
impersonateAccount,
loadFixture,
time,
} from "@nomicfoundation/hardhat-network-helpers";

describe("ForkTestSimulateUpgrade", function () {
Expand Down Expand Up @@ -186,12 +187,19 @@ describe("ForkTestSimulateUpgrade", function () {
);
const [signer] = await ethers.getSigners();
const setProposalGuardianSelector = ethers
.id("_setProposalGuardian(address)")
.id("_setProposalGuardian(address,uint96)")
.substring(0, 10);
const setProposalGuardianData =
setProposalGuardianSelector +
ethers.AbiCoder.defaultAbiCoder()
.encode(["address"], [signer.address])
.encode(
["address", "uint96"],
[
signer.address,
(await time.latest()) +
6 * 30 * 24 * 60 * 60 /* Expire in 6 months */,
]
)
.slice(2);

expect(await governorBravoDelegator.proposalGuardian()).to.equal(
Expand Down
32 changes: 28 additions & 4 deletions test/GovernorBravo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,10 @@ describe("Governor Bravo", function () {
const { governorBravo, otherAccount } = await loadFixture(deployFixtures);
const proposalId = await proposeAndPass(governorBravo);

await governorBravo._setProposalGuardian(otherAccount);
await governorBravo._setProposalGuardian(
otherAccount,
(await time.latest()) + 1000
);
await governorBravo.connect(otherAccount).cancel(proposalId);
});

Expand Down Expand Up @@ -1225,7 +1228,9 @@ describe("Governor Bravo", function () {
it("Set Proposal Guardian: admin only", async function () {
const { governorBravo, otherAccount } = await loadFixture(deployFixtures);
await expect(
governorBravo.connect(otherAccount)._setProposalGuardian(otherAccount)
governorBravo
.connect(otherAccount)
._setProposalGuardian(otherAccount, (await time.latest()) + 1000)
).to.be.revertedWith("GovernorBravo::_setProposalGuardian: admin only");
expect(await governorBravo.proposalGuardian()).to.equal(
ethers.ZeroAddress
Expand All @@ -1234,13 +1239,32 @@ describe("Governor Bravo", function () {

it("Set Proposal Guardian: happy path", async function () {
const { governorBravo, otherAccount } = await loadFixture(deployFixtures);
await expect(governorBravo._setProposalGuardian(otherAccount))
const expiryTimestamp = (await time.latest()) + 1000;
await expect(
governorBravo._setProposalGuardian(otherAccount, expiryTimestamp)
)
.to.emit(governorBravo, "ProposalGuardianSet")
.withArgs(ethers.ZeroAddress, otherAccount.address);
.withArgs(ethers.ZeroAddress, 0, otherAccount.address, expiryTimestamp);
expect(await governorBravo.proposalGuardian()).to.equal(
otherAccount.address
);
});

it("Set Proposal Guardian: removed after expiration", async function () {
const { governorBravo, otherAccount } = await loadFixture(deployFixtures);
const expiryTimestamp = (await time.latest()) + 1000;

await governorBravo._setProposalGuardian(otherAccount, expiryTimestamp);

expect(await governorBravo.proposalGuardian()).to.equal(
otherAccount.address
);

await time.increase(1001);
expect(await governorBravo.proposalGuardian()).to.equal(
ethers.ZeroAddress
);
});
});

describe("Whitelist", function () {
Expand Down

0 comments on commit 05503a7

Please sign in to comment.