Skip to content

Commit

Permalink
Deployment with Governance proposal (#425)
Browse files Browse the repository at this point in the history
* Deployment with Governance proposal

* Fix launch date

* Split proposal

* Full proposal execution flow

* Get rid of hardcoded timestamps

* Update readme

* Tweaks

* Fix rewards fork test

* Round up

* Fix tests

* Make proposal desc more detailed

* Add deployment (#428)
  • Loading branch information
shahthepro authored May 26, 2024
1 parent 012b8ab commit b4b5aa4
Show file tree
Hide file tree
Showing 13 changed files with 442 additions and 171 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ In another terminal:
brownie console --network hardhat-fork
```

## Deploying contracts (with Foundry)
```
$ DEPLOYER_PRIVATE_KEY=$DEPLOYER_PRIVATE_KEY forge script script/deploy/DeployManager.sol:DeployManager --fork-url $ALCHEMY_PROVIDER_URL --slow --legacy --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
```

## Deploying contracts

Setup environment variables:
Expand Down
6 changes: 5 additions & 1 deletion build/deployments.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"1": {
"executions": {
"010_xOGNSetup": 1716312107
"010_xOGNSetup": 1716312107,
"011_OgnOgvMigration": 1716485925
},
"contracts": {
"MIGRATOR": "0x95c347D6214614A780847b8aAF4f96Eb84f4da6d",
"MIGRATOR_IMPL": "0x946e9BED9EDebEBCE95Dea72bDD38F8c3F6efd2E",
"OGN_REWARDS_SOURCE": "0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b",
"OGN_REWARDS_SOURCE_IMPL": "0x16890bdd817Ed1c4654430d67329CB20b0B71bB0",
"VEOGV_IMPL": "0x2D86E0342a0d263Dff712CD0Aa96d075F61974ed",
"XOGN": "0x63898b3b6Ef3d39332082178656E9862bee45C57",
"XOGN_IMPL": "0x97711c7a5D64A064a95d10e37f786d2bD8b1F3c8"
}
Expand Down
186 changes: 186 additions & 0 deletions contracts/utils/GovProposalHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

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

import "OpenZeppelin/openzeppelin-contracts@4.6.0/contracts/utils/Strings.sol";
import {IGovernor} from "OpenZeppelin/openzeppelin-contracts@4.6.0/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) {
bytes memory proposeData = getProposeCalldata(prop);

console.log("----------------------------------");
console.log("Create following tx on Governance:");
console.log("To:", governanceAddr);
console.log("Data:");
console.logBytes(proposeData);
console.log("----------------------------------");

// Proposal doesn't exists, create it
console.log("Creating proposal on fork...");
(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/openzeppelin-contracts@4.6.0/=./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);
}
}
Loading

0 comments on commit b4b5aa4

Please sign in to comment.