Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do Governance Proposals with Signatures #429

Merged
merged 20 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions contracts/utils/GovProposalHelper.sol

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to add the description for the functions and parameters

Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {Addresses} from "contracts/utils/Addresses.sol";
import "forge-std/console.sol";

import "OpenZeppelin/[email protected]/contracts/utils/Strings.sol";
import {IGovernor} from "OpenZeppelin/[email protected]/contracts/governance/IGovernor.sol";
import {Governance} from "../Governance.sol";

import "contracts/utils/VmHelper.sol";

struct GovAction {
address target;
uint256 value;
string fullsig;
bytes data;
}

struct GovProposal {
string description;
GovAction[] actions;
}

library GovProposalHelper {
using VmHelper for Vm;

function id(GovProposal memory prop) internal view returns (uint256 proposalId) {
bytes32 descriptionHash = keccak256(bytes(prop.description));
(address[] memory targets, uint256[] memory values, bytes[] memory calldatas) = getParams(prop);

proposalId = uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
}

function getParams(GovProposal memory prop)
internal
view
returns (address[] memory targets, uint256[] memory values, bytes[] memory calldatas)
{
uint256 actionLen = prop.actions.length;
targets = new address[](actionLen);
values = new uint256[](actionLen);

string[] memory sigs = new string[](actionLen);
bytes[] memory data = new bytes[](actionLen);

for (uint256 i = 0; i < actionLen; ++i) {
targets[i] = prop.actions[i].target;
sigs[i] = prop.actions[i].fullsig;
data[i] = prop.actions[i].data;
values[i] = prop.actions[i].value;
}

calldatas = _encodeCalldata(sigs, data);
}

function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas)
private
pure
returns (bytes[] memory)
{
bytes[] memory fullcalldatas = new bytes[](calldatas.length);

for (uint256 i = 0; i < signatures.length; ++i) {
fullcalldatas[i] = bytes(signatures[i]).length == 0
? calldatas[i]
: abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]);
}

return fullcalldatas;
}

function setDescription(GovProposal storage prop, string memory description) internal {
prop.description = description;
}

function action(GovProposal storage prop, address target, string memory fullsig, bytes memory data) internal {
prop.actions.push(GovAction({target: target, fullsig: fullsig, data: data, value: 0}));
}

function getProposeCalldata(GovProposal memory prop) internal view returns (bytes memory proposeCalldata) {
(address[] memory targets, uint256[] memory values, bytes[] memory calldatas) = getParams(prop);

proposeCalldata = abi.encodeWithSignature(
"propose(address[],uint256[],bytes[],string)", targets, values, calldatas, prop.description
);
}

function impersonateAndSimulate(GovProposal memory prop) internal {
address VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
Vm vm = Vm(VM_ADDRESS);
console.log("Impersonating timelock to simulate governance proposal...");
vm.startPrank(Addresses.TIMELOCK);
for (uint256 i = 0; i < prop.actions.length; i++) {
GovAction memory propAction = prop.actions[i];
bytes memory sig = abi.encodePacked(bytes4(keccak256(bytes(propAction.fullsig))));
(bool success, bytes memory data) = propAction.target.call(abi.encodePacked(sig, propAction.data));
if (!success) {
console.log(propAction.fullsig);
revert("Governance action failed");
}
}
vm.stopPrank();
console.log("Governance proposal simulation complete");
}

function simulate(GovProposal memory prop, address governanceAddr) internal {
address VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
Vm vm = Vm(VM_ADDRESS);

uint256 proposalId = id(prop);

Governance governance = Governance(payable(governanceAddr));

vm.startPrank(Addresses.GOV_MULTISIG);

uint256 snapshot = governance.proposalSnapshot(proposalId);

if (snapshot == 0) {
// Proposal doesn't exists, create it
console.log("Creating proposal...");
bytes memory proposeData = getProposeCalldata(prop);
(bool success, bytes memory data) = governanceAddr.call(proposeData);
}

IGovernor.ProposalState state = governance.state(proposalId);

if (state == IGovernor.ProposalState.Executed) {
// Skipping executed proposal
return;
}

if (state == IGovernor.ProposalState.Pending) {
console.log("Waiting for voting period...");
// Wait for voting to start
vm.roll(block.number + 10);
vm.warp(block.timestamp + 10 minutes);

state = governance.state(proposalId);
}

if (state == IGovernor.ProposalState.Active) {
console.log("Voting on proposal...");
// Vote on proposal
governance.castVote(proposalId, 1);
// Wait for voting to end
vm.roll(governance.proposalDeadline(proposalId) + 20);
vm.warp(block.timestamp + 2 days);

state = governance.state(proposalId);
}

if (state == IGovernor.ProposalState.Succeeded) {
console.log("Queuing proposal...");
governance.queue(proposalId);

state = governance.state(proposalId);
}

if (state == IGovernor.ProposalState.Queued) {
console.log("Executing proposal");
// Wait for timelock
vm.roll(governance.proposalEta(proposalId) + 20);
vm.warp(block.timestamp + 2 days);

governance.execute(proposalId);

state = governance.state(proposalId);
}

if (state != IGovernor.ProposalState.Executed) {
revert("Unexpected proposal state");
}

vm.stopPrank();
}
}
21 changes: 21 additions & 0 deletions contracts/utils/VmHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import "forge-std/Vm.sol";

library VmHelper {
function getVM() internal view returns (Vm vm) {
address VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
vm = Vm(VM_ADDRESS);
}

function isForkEnv(Vm vm) public view returns (bool) {
return vm.isContext(VmSafe.ForgeContext.ScriptDryRun) || vm.isContext(VmSafe.ForgeContext.Test)
|| vm.isContext(VmSafe.ForgeContext.TestGroup);
}

function isTestEnv(Vm vm) public view returns (bool) {
return vm.isContext(VmSafe.ForgeContext.Test) || vm.isContext(VmSafe.ForgeContext.TestGroup);
}
}
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ remappings = [
"contracts/=./contracts",
"script/=./script",
"tests/=./tests",
"utils/=./contracts/utils",
"OpenZeppelin/openzeppelin-contracts@02fcc75bb7f35376c22def91b0fb9bc7a50b9458/=./lib/openzeppelin-contracts",
"OpenZeppelin/openzeppelin-contracts-upgradeable@a16f26a063cd018c4c986832c3df332a131f53b9/=./lib/openzeppelin-contracts-upgradeable",
"OpenZeppelin/[email protected]/=./lib/openzeppelin-contracts",
Expand Down
31 changes: 31 additions & 0 deletions script/ExtraOGNForMigration.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import "forge-std/Script.sol";
import {Addresses} from "contracts/utils/Addresses.sol";
import {IMintableERC20} from "contracts/interfaces/IMintableERC20.sol";
import {RewardsSource} from "contracts/RewardsSource.sol";

contract ExtraOGNForMigration is Script {
uint256 constant OGN_EPOCH = 1717041600; // May 30, 2024 GMT

// Ref: https://snapshot.org/#/origingov.eth/proposal/0x741893a4d9838c0b69fac03650756e21fe00ec35b5309626bb0d6b816f861f9b
uint256 public constant OGN_MINTED = 409_664_846 ether;

function run() external {
vm.warp(OGN_EPOCH);

IMintableERC20 ogv = IMintableERC20(Addresses.OGV);

uint256 rewards = RewardsSource(Addresses.OGV_REWARDS_PROXY).previewRewards();

uint256 ogvSupply = ogv.totalSupply();
uint256 maxOgnNeeded = ((ogvSupply + rewards) * 0.09137 ether) / 1 ether;

console.log("OGV Supply", ogvSupply / 1 ether);
console.log("Pending OGV Rewards", rewards / 1 ether);
console.log("Max OGN Needed", maxOgnNeeded / 1 ether);
console.log("OGN from Treasury", (maxOgnNeeded - OGN_MINTED) / 1 ether);
}
}
19 changes: 11 additions & 8 deletions script/deploy/DeployManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,26 @@ import {XOGNSetupScript} from "./mainnet/010_xOGNSetupScript.sol";
import {OgnOgvMigrationScript} from "./mainnet/011_OgnOgvMigrationScript.sol";
import {XOGNGovernanceScript} from "./mainnet/012_xOGNGovernanceScript.sol";

import "contracts/utils/VmHelper.sol";

contract DeployManager is Script {
using VmHelper for Vm;

mapping(string => address) public deployedContracts;
mapping(string => bool) public scriptsExecuted;

string internal forkFileId = "";

function isForked() public view returns (bool) {
return vm.isContext(VmSafe.ForgeContext.ScriptDryRun) || vm.isContext(VmSafe.ForgeContext.Test)
|| vm.isContext(VmSafe.ForgeContext.TestGroup);
}

function getDeploymentFilePath() public view returns (string memory) {
return isForked() ? getForkDeploymentFilePath() : getMainnetDeploymentFilePath();
return vm.isForkEnv() ? getForkDeploymentFilePath() : getMainnetDeploymentFilePath();
}

function getMainnetDeploymentFilePath() public view returns (string memory) {
return string(abi.encodePacked(vm.projectRoot(), "/build/deployments.json"));
}

function getForkDeploymentFilePath() public view returns (string memory) {
return string(abi.encodePacked(vm.projectRoot(), "/build/deployments-fork-", forkFileId, ".json"));
return string(abi.encodePacked(vm.projectRoot(), "/build/deployments-fork", forkFileId, ".json"));
}

function setUp() external {
Expand All @@ -53,7 +52,7 @@ contract DeployManager is Script {
);
}

if (isForked()) {
if (vm.isForkEnv()) {
// Duplicate Mainnet File
vm.writeFile(getForkDeploymentFilePath(), vm.readFile(mainnetFilePath));
}
Expand All @@ -67,6 +66,10 @@ contract DeployManager is Script {
}

function _runDeployFile(BaseMainnetScript deployScript) internal {
if (deployScript.skip()) {
return;
}

string memory chainIdStr = Strings.toString(block.chainid);
string memory chainIdKey = string(abi.encodePacked(".", chainIdStr));

Expand Down
Loading
Loading