Skip to content

feat: Governor basic #13

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

Merged
merged 23 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
43fbcdf
feat: add viem to the project
TravellerOnTheRun Jun 19, 2024
e7bf9cd
chore: setup format on save for prettier vscode
TravellerOnTheRun Jun 19, 2024
a1655a6
TODO: finish Should transfer tokens between accounts
TravellerOnTheRun Jun 19, 2024
a73105d
refactor: RIF Token test to use viem
TravellerOnTheRun Jun 21, 2024
f2a1190
lint: unused import
TravellerOnTheRun Jun 21, 2024
85206ba
feat: Timelock and Governor contracts
TravellerOnTheRun Jun 25, 2024
ee645b7
chore: vscode and hardhat settings
TravellerOnTheRun Jun 25, 2024
4bbd22c
feat: deplyRif function
TravellerOnTheRun Jun 25, 2024
3b43ee6
refactor: log address of veRIF
TravellerOnTheRun Jun 25, 2024
50d74c6
feat: use deployRif function in deploy
TravellerOnTheRun Jun 25, 2024
b42b722
feat: deployGovernor and deployment test
TravellerOnTheRun Jun 25, 2024
86b557b
feat: base governor setup tests
TravellerOnTheRun Jun 25, 2024
7a71e55
chore: add bigint-crypto-utils
TravellerOnTheRun Jun 25, 2024
cae8e4e
feat: add faucet to deploy-rif
TravellerOnTheRun Jun 25, 2024
d822c0f
feat: add error desc to TokenFaucet
TravellerOnTheRun Jun 27, 2024
25d1ea6
feat: deploy TokenFaucet along with RIF for testing
TravellerOnTheRun Jun 27, 2024
73ba30e
type: add typing fo Governor
TravellerOnTheRun Jun 27, 2024
10f5cf8
test: proposal creation and cast vote
TravellerOnTheRun Jun 27, 2024
2a0e5a5
test: unite Governance test cases
TravellerOnTheRun Jun 27, 2024
747bfa1
fix: lint erros and sol compiler errors
TravellerOnTheRun Jun 27, 2024
9ae78da
fix: DAOTimelock version and absence of bigint-crypto-utils
TravellerOnTheRun Jun 27, 2024
e876e09
fix: slither findings
cybervoid0 Jun 28, 2024
9a53cfd
Merge pull request #15 from rsksmart/governor-fixes
TravellerOnTheRun Jul 1, 2024
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
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"solidity.compileUsingRemoteVersion": "v0.8.24+commit.e11b9ed9"
"solidity.compileUsingRemoteVersion": "v0.8.24+commit.e11b9ed9",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
23 changes: 23 additions & 0 deletions contracts/DaoTimelockUpgradable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

Check warning on line 3 in contracts/DaoTimelockUpgradable.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol";

Check warning on line 4 in contracts/DaoTimelockUpgradable.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

contract DaoTimelockUpgradable is
UUPSUpgradeable,
TimelockControllerUpgradeable
{
function initialize(
uint256 minDelay,
address[] memory proposers,
address[] memory executors,
address admin
) public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
__TimelockController_init(minDelay, proposers, executors, admin);
}
function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}

Check warning on line 22 in contracts/DaoTimelockUpgradable.sol

View workflow job for this annotation

GitHub Actions / test

Code contains empty blocks
}
144 changes: 144 additions & 0 deletions contracts/Governor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";

Check warning on line 4 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol";

Check warning on line 5 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorCountingSimpleUpgradeable.sol";

Check warning on line 6 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorCountingSimpleUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorStorageUpgradeable.sol";

Check warning on line 7 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorStorageUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol";

Check warning on line 8 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";

Check warning on line 9 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol";

Check warning on line 10 in contracts/Governor.sol

View workflow job for this annotation

GitHub Actions / test

global import of path @openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract RootDao is
Initializable,
GovernorUpgradeable,
GovernorSettingsUpgradeable,
GovernorCountingSimpleUpgradeable,
GovernorStorageUpgradeable,
GovernorVotesUpgradeable,
GovernorVotesQuorumFractionUpgradeable,
GovernorTimelockControlUpgradeable,
OwnableUpgradeable,
UUPSUpgradeable
{
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(
IVotes voteToken,
TimelockControllerUpgradeable timelockController,
address initialOwner
) public initializer {
__Governor_init("RootDao");
__GovernorSettings_init(7200 /* 1 day */, 50400 /* 1 week */, 10 * 10 ** 18);
__GovernorCountingSimple_init();
__GovernorStorage_init();
__GovernorVotes_init(voteToken);
__GovernorVotesQuorumFraction_init(4);
__GovernorTimelockControl_init(timelockController);
__Ownable_init(initialOwner);
__UUPSUpgradeable_init();
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

// The following functions are overrides required by Solidity.

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

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

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

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

function proposalNeedsQueuing(
uint256 proposalId
) public view override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (bool) {
return super.proposalNeedsQueuing(proposalId);
}

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

function _propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
address proposer
) internal override(GovernorUpgradeable, GovernorStorageUpgradeable) returns (uint256) {
return super._propose(targets, values, calldatas, description, proposer);
}

function _queueOperations(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (uint48) {
return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash);
}

function _executeOperations(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) {
super._executeOperations(proposalId, targets, values, calldatas, descriptionHash);
}

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

function _executor()
internal
view
override(GovernorUpgradeable, GovernorTimelockControlUpgradeable)
returns (address)
{
return super._executor();
}
}
4 changes: 4 additions & 0 deletions contracts/RIFToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ pragma solidity ^0.4.24;

import "rif-token-contracts/contracts/RIF/RIFToken.sol";

contract RIFTokenContact is RIFToken {

}

/* File must exist, else test/RIFToken.test.ts will fail */
52 changes: 52 additions & 0 deletions contracts/TokenFaucet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @title RIF Token Faucet
* @author IOV Labs
* @notice Original source code is taken from the repository:
* https://github.com/riflabs/rif-faucet/blob/master/contracts/TokenFaucet.sol
*/

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract TokenFaucet is Ownable {
IERC20 public immutable tokenContract;

mapping(address => uint) cannotDispenseUntil;

uint256 public dispenseValue = 10 * 10 ** 18; // 10 tRIFs
uint256 public dispenceFrequency = 1 hours;

event DispenceFrequencyChanged(address changer, uint256 oldValue, uint256 newValue);
event DispenceValueChanged(address changer, uint256 oldValue, uint256 newValue);

modifier canDispense(address to) {
require(cannotDispenseUntil[to] < block.timestamp, "CANNOT DISPENSE MORE THAN 1 TIME PER HOUR");
_;
}

constructor(IERC20 rifToken) Ownable(msg.sender) {
tokenContract = rifToken;
}

function recover() public returns (bool) {
uint256 totalAmount = tokenContract.balanceOf(address(this));
return tokenContract.transfer(owner(), totalAmount);
}

function dispense(address to) public canDispense(to) returns (bool) {
cannotDispenseUntil[to] = block.timestamp + dispenceFrequency;
return tokenContract.transfer(to, dispenseValue);
}

function setDispenseValue(uint256 value) public onlyOwner {
emit DispenceValueChanged(msg.sender, dispenseValue, value);
dispenseValue = value;
}

function setDispenseFrequency(uint256 freqSeconds) public onlyOwner {
emit DispenceFrequencyChanged(msg.sender, dispenceFrequency, freqSeconds);
dispenceFrequency = freqSeconds;
}
}
6 changes: 6 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HardhatUserConfig } from 'hardhat/config'
import '@nomicfoundation/hardhat-toolbox'
import '@openzeppelin/hardhat-upgrades'
import '@nomicfoundation/hardhat-viem'

const config: HardhatUserConfig = {
solidity: {
Expand All @@ -14,6 +15,11 @@ const config: HardhatUserConfig = {
{ version: '0.4.24' },
],
},
networks: {
hardhat: {
allowUnlimitedContractSize: true,
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of doing this I would recommend using optimizer with 200 runs because otherwise you won't be able to deploy it on Rootstock!

}

export default config
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
"author": "RootstockLabs",
"license": "MIT",
"scripts": {
"compile": "hardhat compile",
"compile": "npx hardhat compile",
"clean": "hardhat clean",
"lint:ts": "eslint 'test/**/*.ts'",
"lint:sol": "solhint 'contracts/**/*.sol'",
"lint": "yarn lint:ts && yarn lint:sol",
"test": "hardhat test"
"test": "npx hardhat test"
},
"devDependencies": {
"@eslint/js": "^9.5.0",
Expand All @@ -21,7 +21,9 @@
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-toolbox-viem": "^3.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@nomicfoundation/hardhat-viem": "^2.0.2",
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@openzeppelin/hardhat-upgrades": "^3.2.0",
Expand All @@ -33,6 +35,7 @@
"@types/node": ">=18.0.0",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.11.0",
"bigint-crypto-utils": "^3.3.0",
"chai": "^4.2.0",
"eslint": "^9.5.0",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -48,7 +51,8 @@
"ts-node": ">=8.0.0",
"typechain": "^8.3.2",
"typescript": "^5.4.5",
"typescript-eslint": "^7.14.1"
"typescript-eslint": "^7.14.1",
"viem": "^2.15.1"
},
"dependencies": {
"rif-token-contracts": "https://github.com/Freshenext/RIF-Token"
Expand Down
29 changes: 29 additions & 0 deletions scripts/deploy-governor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import hre, { ethers, upgrades } from 'hardhat'
import { GovernorTimelockControlUpgradeable, RootDao } from '../typechain-types'

export const deployGovernor = async (tokenAddress: string, deployerAddress: string) => {
const RootDAOFactory = await ethers.getContractFactory('RootDao')
const TimelockFactory = await ethers.getContractFactory('DaoTimelockUpgradable')
// TODO: figure out why it allows to put only a single argument
const timelock = (await upgrades.deployProxy(TimelockFactory, [1, [], [], ethers.ZeroAddress], {
initializer: 'initialize(uint256,address[],address[],address)',
kind: 'uups',
timeout: 0,
})) as unknown as GovernorTimelockControlUpgradeable

const rootDAOGovernor = (await upgrades.deployProxy(
RootDAOFactory,
[tokenAddress, await timelock.getAddress(), deployerAddress],
{
initializer: 'initialize',
kind: 'uups',
timeout: 0,
},
)) as unknown as RootDao

const rootDAOContact = await rootDAOGovernor.waitForDeployment()

console.log(`Deployed Governor on ${hre.network.name} with address ${await rootDAOContact.getAddress()}`)

return rootDAOContact
}
36 changes: 36 additions & 0 deletions scripts/deploy-rif.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'
import hre, { ethers } from 'hardhat'

export const deployRif = async (deployer: SignerWithAddress) => {
const rifToken = await ethers.deployContract('RIFToken')
await rifToken.waitForDeployment()

const rifAddress = await rifToken.getAddress()

console.log(`Deployed RIF Token on ${hre.network.name} with address ${rifAddress}`)

// `setAuthorizedManagerContract` transfers all tokens to Deployer
const tx1 = await rifToken.setAuthorizedManagerContract(deployer.address)
await tx1.wait()
console.log(`All RIF tokens transferred to ${deployer.address}`)

// close distribution
const block = await ethers.provider.getBlock('latest')
const now = Math.round(Date.now() / 1000)
const tx2 = await rifToken.closeTokenDistribution(block?.timestamp ?? now)
await tx2.wait()
console.log(`RIF distribution closed`)

const tokenFaucet = await (await ethers.deployContract('TokenFaucet', [rifToken])).waitForDeployment()

// transfer half of RIFs to the faucet
const rifSupply = 10n ** 27n

const tx3 = await rifToken.transfer(await tokenFaucet.getAddress(), rifSupply / 2n)
await tx3.wait()
console.log(`RIF tokens transferred to the Faucet`)

console.log(`Deployed Token Faucet on ${hre.network.name} with address ${await tokenFaucet.getAddress()}`)

return { rifToken, rifAddress, tokenFaucet }
}
12 changes: 9 additions & 3 deletions scripts/deploy-stRIF.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ethers, upgrades } from 'hardhat'
import hre, { ethers, upgrades } from 'hardhat'
import { StRIFToken } from '../typechain-types'

export const deployStRIF = async (rifTokenAddress: string, deployerAddress: string) => {
Expand All @@ -8,7 +8,13 @@ export const deployStRIF = async (rifTokenAddress: string, deployerAddress: stri
kind: 'uups',
timeout: 0, // wait indefinitely
unsafeAllow: ['internal-function-storage'],
}) as unknown as StRIFToken)
})) as unknown as StRIFToken

return await stRIFToken.waitForDeployment()
const stRIFContract = await stRIFToken.waitForDeployment()

console.log(
`Deployed RIF Governance Token on ${hre.network.name} with address ${await stRIFContract.getAddress()}`,
)

return stRIFContract
}
Loading
Loading