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

Dmtrbch fxd 116 transfer ownership to dao #317

Open
wants to merge 3 commits into
base: xdc-prod-hardhat
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion add-collateral.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"51":{"tokenAddress":"0x0000000000000000000000000000000000000000","testOracle":"0x0000000000000000000000000000000000000000","fathomProxyFactory":"0x0000000000000000000000000000000000000000","fathomProxyAdmin":"0x0000000000000000000000000000000000000000"},"31337":{"tokenAddress":"0x22879cD092f74aBA6d198B053f25b7e26BBbd5Da","testOracle":"0x828e4F72BC7B912f6Fde900071212aAA075BBd22","fathomProxyFactory":"0x13cdf6cA2C1c216198FB51A4c515FDa6F11B9dE0","fathomProxyAdmin":"0x28c6408131836B0f1Adbae443b65B76487723C4b"},"token":"GLD"}
{"51":{"tokenAddress":"0x0000000000000000000000000000000000000000","testOracle":"0x0000000000000000000000000000000000000000","fathomProxyFactory":"0x0000000000000000000000000000000000000000","fathomProxyAdmin":"0x0000000000000000000000000000000000000000"},"31337":{"tokenAddress":"0x30087319dCCa6436de3803EED6B8E67FaaCE36aD","testOracle":"0x11897e6e50C42067008Ca9C93095324aE5d04Cb1","fathomProxyFactory":"0xB14d2dac60617bd7B6e67D374daFBf298Ca7DD7b","fathomProxyAdmin":"0xc9610b7E6973F7162A682279D0abF93F677dc358"},"token":"GLD"}
103 changes: 103 additions & 0 deletions contracts/main/dao/governance/ProtocolGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {Governor} from "@openzeppelin/contracts/governance/Governor.sol";
import {GovernorSettings} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import {GovernorCountingSimple} from "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import {GovernorVotesQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import {GovernorTimelockControl} from "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";

contract ProtocolGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
constructor(IVotes _token, TimelockController _timelock)
Governor("ProtocolGovernor")
GovernorSettings(1, /* 1 block */ 50400, /* 1 week */ 0)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4)
GovernorTimelockControl(_timelock)
{}

// The following functions are overrides required by Solidity.

function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) {
return super.votingDelay();
}

function votingPeriod() public view override(IGovernor, GovernorSettings) returns (uint256) {
return super.votingPeriod();
}

function quorum(uint256 blockNumber)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}

function state(uint256 proposalId)
public
view
override(Governor, GovernorTimelockControl)
returns (ProposalState)
{
return super.state(proposalId);
}

function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override(Governor, IGovernor) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}

function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}

function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}

function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}

function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}

function supportsInterface(bytes4 interfaceId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
15 changes: 15 additions & 0 deletions contracts/main/dao/governance/Timelock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

contract Timelock is TimelockController {

// minDelay is how long you have to wait before executing
// proposers is the list of addresses that can propose
// executors is the list of addresses that can execute
// admin is msg.sender - we must give in an admin at first, so that we can move timelock controller admin so only the DAO can do anything with the timelock contorller
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors)
TimelockController(minDelay, proposers, executors, msg.sender)
{}
}
28 changes: 28 additions & 0 deletions contracts/main/dao/token/GovToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

contract GovToken is ERC20, ERC20Permit, ERC20Votes {
constructor() ERC20("Fathom Protocol Vote Token", "VFTHM") ERC20Permit("MyToken") {}

// The following functions are overrides required by Solidity.

function mint(address to, uint256 amount) public {
_mint(to, amount);
}

function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
super._afterTokenTransfer(from, to, amount);
}

function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
super._mint(to, amount);
}

function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
super._burn(account, amount);
}
}
6 changes: 6 additions & 0 deletions deploy/deploy-main/01_deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const { configureShowStopper } = require("../../scripts/setup/deploy/configureSh
const { deployVault } = require("../../scripts/setup/deploy/deployVault");
const { initCollateralTokenAdapter } = require("../../scripts/setup/deploy/initCollateralTokenAdapter");
const { configFlashLending } = require("../../scripts/setup/deploy/configFlashLending");
const { deployDao } = require("../../scripts/setup/deploy/deployDao");
const { transferOwnership } = require("../../scripts/setup/transfer-ownership/transferOwnership");

// Configuration
const { addCollateralPools } = require("../../scripts/configuration/deploy/addCollateralPools");
Expand All @@ -26,6 +28,10 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {

// Configuration
await addCollateralPools(deployments, getChainId);

//Transfer ownership of the contracts to the dao
await deployDao(getNamedAccounts, deployments);
await transferOwnership(getNamedAccounts, deployments);
};

module.exports.tags = ["DeployMain"];
8 changes: 8 additions & 0 deletions deploy/deploy-test-fixture/01_deploy_test_fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const { initialize: initializeAddCollateral } = require("../../scripts/setup/add
const { addRoles: addRolesAddCollateral } = require("../../scripts/setup/add-collateral/addRoles");
const { deployVault: deployVaultAddCollateral } = require("../../scripts/setup/add-collateral/deployVault");

// Deploy DAO
const { deployDao } = require("../../scripts/setup/deploy/deployDao");
const { transferOwnership } = require("../../scripts/setup/transfer-ownership/transferOwnership");

module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {
// Setup
await deployMocks(getNamedAccounts, deployments, getChainId);
Expand Down Expand Up @@ -63,6 +67,10 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {

// Configuration
await addCollateralConfigPool(deployments, getChainId);

// Transfer ownership of the contracts to the dao
await deployDao(getNamedAccounts, deployments);
await transferOwnership(getNamedAccounts, deployments, true);
};

module.exports.tags = ["DeployTestFixture"];
1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require("@nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");
require("./tasks/price-feed");
require("./tasks/fathom-solidity-sdk");
require("./tasks/fthm");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
Expand Down
63 changes: 63 additions & 0 deletions scripts/setup/deploy/deployDao.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const { ethers } = require("hardhat");
const provider = ethers.provider;

const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";

const PROPOSER_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("PROPOSER_ROLE"));
const EXECUTOR_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("EXECUTOR_ROLE"));
const TIMELOCK_ADMIN_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("TIMELOCK_ADMIN_ROLE"));

async function deployDao(getNamedAccounts, deployments) {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();

// Deploy token for voting
// Token should only be deployed for local tests, otherwise use the FTHM token deployed on mainnet/testnet
await deploy("GovToken", {
from: deployer,
args: [],
log: true,
});

const GovToken = await deployments.get("GovToken");
const govToken = await ethers.getContractAt("GovToken", GovToken.address);
await govToken.mint(deployer, ethers.utils.parseEther("100"));

await govToken.connect(provider.getSigner(deployer)).delegate(deployer);

// Deploy Timelock
// initial minimum delay for operations
// after a vote passes, we have 1 day before we can enact
const MIN_DELAY = ethers.BigNumber.from("3600"); // 1 hour TODO: check this value

await deploy("Timelock", {
from: deployer,
args: [
MIN_DELAY,
[], // proposers
[], // executors
],
log: true,
});

const Timelock = await deployments.get("Timelock");

// Deploy Governor
await deploy("ProtocolGovernor", {
from: deployer,
args: [GovToken.address, Timelock.address],
log: true,
});

const ProtocolGovernor = await deployments.get("ProtocolGovernor");

const timelock = await ethers.getContractAt("Timelock", Timelock.address);

await timelock.grantRole(PROPOSER_ROLE, ProtocolGovernor.address); // with this we are saying that everyone can propose, but only governor can queue
await timelock.grantRole(EXECUTOR_ROLE, ZERO_ADDRESS); // anybody can execute and pass proposals
await timelock.revokeRole(TIMELOCK_ADMIN_ROLE, deployer); // deployer will no longer be the admin of the timelock
}

module.exports = {
deployDao,
};
55 changes: 55 additions & 0 deletions scripts/setup/transfer-ownership/transferOwnership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { ethers } = require("hardhat");
const { BigNumber } = ethers;

const { getProxy } = require("../../../common/proxies");

async function transferOwnership(getNamedAccounts, deployments, forFixture = false) {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();

const ProxyFactory = await deployments.get("FathomProxyFactory");
const proxyFactory = await ethers.getContractAt("FathomProxyFactory", ProxyFactory.address);

const ProxyAdmin = await deployments.get("FathomProxyAdmin");
const proxyAdmin = await ethers.getContractAt("FathomProxyAdmin", ProxyAdmin.address);

const flashMintArbitrager = await getProxy(proxyFactory, "FlashMintArbitrager");
const proxyWalletFactory = await getProxy(proxyFactory, "ProxyWalletFactory");
const bookKeeperFlashMintArbitrager = await getProxy(proxyFactory, "BookKeeperFlashMintArbitrager");
const accessControlConfig = await getProxy(proxyFactory, "AccessControlConfig");
const fathomStablecoin = await getProxy(proxyFactory, "FathomStablecoin");
const stableSwap = await getProxy(proxyFactory, "StableSwapModule");
const stableSwapModuleWrapper = await getProxy(proxyFactory, "StableSwapModuleWrapper");

const Timelock = await deployments.get("Timelock");

// Transfer ownership of FathomProxyFactory to DAO
await proxyFactory.transferOwnership(Timelock.address);
// Transfer ownership of FathomProxyAdmin to DAO
await proxyAdmin.transferOwnership(Timelock.address);
// Transfer ownership of ProxyWalletFactory to DAO
// await proxyWalletFactory.transferOwnership(Timelock.address); TODO: issue since owner is not set on ProxyWalletFactory
// Transfer ownership of FlashMintArbitrager to DAO
await flashMintArbitrager.transferOwnership(Timelock.address);
// Transfer ownership of BookKeeperFlashMintArbitrager to DAO
await bookKeeperFlashMintArbitrager.transferOwnership(Timelock.address);

if (forFixture) {
await fathomStablecoin.grantRole(await fathomStablecoin.MINTER_ROLE(), Timelock.address);
await accessControlConfig.grantRole(await accessControlConfig.PRICE_ORACLE_ROLE(), Timelock.address);
await accessControlConfig.grantRole(await accessControlConfig.OWNER_ROLE(), Timelock.address);
await accessControlConfig.grantRole(await accessControlConfig.MINTABLE_ROLE(), Timelock.address);

await stableSwap.addToWhitelist(Timelock.address);
await stableSwapModuleWrapper.addToWhitelist(Timelock.address);
}

// Grant OWNER_ROLE to DAO
await accessControlConfig.grantRole(await accessControlConfig.OWNER_ROLE(), Timelock.address);
// EOA should renounce ownership
await accessControlConfig.renounceRole(await accessControlConfig.OWNER_ROLE(), deployer);
}

module.exports = {
transferOwnership,
};
42 changes: 42 additions & 0 deletions tasks/fthm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
task("fthm", "FTHM Token Info").setAction(async () => {
const proxyWalletFactory = await ethers.getContractAt("ProxyWalletFactory", "0xA43529Ce149924051e9A56b2BE740AEDbA4A581a");
const adminControls = await ethers.getContractAt("AdminControls", "0x32A5f5D0BdB48E0A3A79ec21364e2F9f3f6a23c5");
const flashMintArbitrager = await ethers.getContractAt("FlashMintArbitrager", "0x25464a1Cf25D1b180a36417fAB9FFd9960627860");
const bookKeeperFlashMintArbitrager = await ethers.getContractAt("BookKeeperFlashMintArbitrager", "0xf3D403DA1C8368Ce164dDA5bd316d582aC457a35");

console.log(await proxyWalletFactory.owner());
console.log(await adminControls.owner());
console.log(await flashMintArbitrager.owner());
console.log(await bookKeeperFlashMintArbitrager.owner());

// const strategies = [
// ethers.utils.getAddress("0x66B45F20cE90D5164bf85C582002d3620C429496"),
// ethers.utils.getAddress("0x465aeF54f7a8d9fE22B74A27fcd922c95cAaD4Be"),
// ethers.utils.getAddress("0x524ae63AB2D30853578E75eA0A758E0DA2d59814"),
// ethers.utils.getAddress("0xfe5037504E0EF5eC2DfBEEA03f9d9cB43580EF23")
// ]
// const accessControlConfig = await ethers.getContractAt("AccessControlConfig", "0x2cD89769a2D9d992790e76c6A9f55c39fdf2FDc2");
// const collateralTokenAdapterXDC = await ethers.getContractAt("CollateralTokenAdapter", "0x2fc7e65023aFF27FA61A573B5C8E3fDe3CE9ef79");
// const collateralTokenAdapterCGO = await ethers.getContractAt("CollateralTokenAdapter", "0x30c64659AADD8C92328859A1CEE99721083A8E0f");

// for (let i = 0; i < strategies.length; i++) {
// console.log("For Strategy", strategies[i]);
// console.log("has ownerRole", await accessControlConfig.hasRole(await accessControlConfig.OWNER_ROLE(), strategies[i]));
// console.log("has govRole", await accessControlConfig.hasRole(await accessControlConfig.GOV_ROLE(), strategies[i]));
// console.log("has priceOracle", await accessControlConfig.hasRole(await accessControlConfig.PRICE_ORACLE_ROLE(), strategies[i]));
// console.log("has adapterRole", await accessControlConfig.hasRole(await accessControlConfig.ADAPTER_ROLE(), strategies[i]));
// console.log("has liquidationEngineRole", await accessControlConfig.hasRole(await accessControlConfig.LIQUIDATION_ENGINE_ROLE(), strategies[i]));
// console.log("has stabilityFeeCollector", await accessControlConfig.hasRole(await accessControlConfig.STABILITY_FEE_COLLECTOR_ROLE(), strategies[i]));
// console.log("has showStopper", await accessControlConfig.hasRole(await accessControlConfig.SHOW_STOPPER_ROLE(), strategies[i]));
// console.log("has positionManager", await accessControlConfig.hasRole(await accessControlConfig.POSITION_MANAGER_ROLE(), strategies[i]));
// console.log("has mintableRole", await accessControlConfig.hasRole(await accessControlConfig.MINTABLE_ROLE(), strategies[i]));
// console.log("has bookKeeperRole", await accessControlConfig.hasRole(await accessControlConfig.BOOK_KEEPER_ROLE(), strategies[i]));
// console.log("has collateralManagerRole", await accessControlConfig.hasRole(await accessControlConfig.COLLATERAL_MANAGER_ROLE(), strategies[i]));
// console.log("is whitelisted in xdc collateral token adapter", await collateralTokenAdapterXDC.whiteListed(strategies[i]));
// console.log("is whitelisted cgo collateral token adapter", await collateralTokenAdapterCGO.whiteListed(strategies[i]));
// console.log('************************************************');
// console.log(`\n`);
// }
});

module.exports = {};
Loading