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

Deployment with Governance proposal #425

Merged
merged 12 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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/[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) {
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/[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);
}
}
Loading
Loading