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

TIP_123: Revoke governance #1097

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e59f1d8
feat: initial commit, setup proposals
thomas-waite Oct 5, 2022
2938b6e
feat: deploy burner timelocks, update CR
thomas-waite Oct 5, 2022
72380a2
feat: revoke non-final roles
thomas-waite Oct 5, 2022
16cea85
feat: transfer dao timelock admin to burner, verify
thomas-waite Oct 5, 2022
94feb8a
feat: deprecate tribe minter
thomas-waite Oct 5, 2022
2530c1b
feat: burn fei labs tribe timelock
thomas-waite Oct 5, 2022
18ae744
deps: deprecate more contracts
thomas-waite Oct 5, 2022
fe7bc94
test: remove non-functional tests
thomas-waite Oct 5, 2022
75940a9
Merge branch 'develop' into feat/revoke-governance
thomas-waite Oct 5, 2022
463ab8e
fix: fix roles e2e tribe minter address
thomas-waite Oct 5, 2022
afa6c38
feat: update with proposal execution
thomas-waite Oct 9, 2022
181acb4
test: remove veBalHelper tests
thomas-waite Oct 9, 2022
c278b78
deps: deprecate more contracts
thomas-waite Oct 9, 2022
5f02fcb
refactor: proposal verify, update mainnet addresses
thomas-waite Oct 9, 2022
2b74508
feat: undelegate from Rari Tribe
thomas-waite Oct 10, 2022
a902d30
test: update roles test, now global roles gone
thomas-waite Oct 10, 2022
8820f39
test: verify can not queue DAO proposals
thomas-waite Oct 10, 2022
e48ddbb
test: very can not execute proposal
thomas-waite Oct 10, 2022
c86a04c
feat: deploy contracts
thomas-waite Oct 10, 2022
cf9d4a8
test: re-add veBal tests, update blockNum
thomas-waite Oct 12, 2022
12e8bb4
test: update redeemer tests now OTC DAI is there
thomas-waite Oct 12, 2022
391da85
test: update veBAL tests
thomas-waite Oct 12, 2022
3d25867
Merge branch 'develop' into feat/revoke-governance
thomas-waite Oct 14, 2022
a2007d4
deps: update block num
thomas-waite Oct 17, 2022
71a5c13
deps: deprecate more contracts after Rari vote
thomas-waite Oct 17, 2022
96b2e5b
deps: deprecate FuseAdmin, FuseGuardian
thomas-waite Oct 17, 2022
d62f2c7
deps: PR review feedback
thomas-waite Oct 18, 2022
03c2a2e
test: update tribe redeemer dai
thomas-waite Oct 18, 2022
72a9cb8
test: update limit
thomas-waite Oct 18, 2022
250f80d
test: verify permissionless tribe undeletation
thomas-waite Oct 21, 2022
3c7499d
test: update veBal boost test
thomas-waite Oct 21, 2022
dec1c50
feat: update block num
thomas-waite Oct 25, 2022
8d9364c
test: update token test account
thomas-waite Oct 25, 2022
18ba908
test: fix last tests
thomas-waite Oct 25, 2022
d861258
refactor: renounce proxyAdmin owner, small updates
thomas-waite Oct 26, 2022
5529c72
test: verify permissionless fei burn, tribe send
thomas-waite Oct 26, 2022
c6a8871
docs: add proposal text
thomas-waite Oct 27, 2022
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 block.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
15746734
15825392
1 change: 1 addition & 0 deletions contracts/test/integration/fixtures/MainnetAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@ library MainnetAddresses {
address public constant RARI_TRIBE_TOKEN_TIMELOCK = 0x625cf6AA7DafB154F3Eb6BE87592110e30290dEe;
address public constant FEI_LABS_CONTRACT = 0x38AfBF8128cc54323e216ACde9516d281c4F1E5f;
address public constant TRIBE_FEI_LABS_DEL = 0x66b9D411E14FBc86424367b67933945fd7E40B11;
address public constant TRIBE_FEI_LABS_DEL_2 = 0xb81cf4981Ef648aaA73F07a18B03970f04d5D8bF;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ contract TribeTimelockedDelegatorBurnerIntegrationTest is DSTest {
tribeTimelock.setPendingBeneficiary(address(tribeTimelockBurner));

// Undelegate some TRIBE to make enough available for withdrawals
vm.prank(tribeTimelock.beneficiary());
vm.startPrank(tribeTimelock.beneficiary());
tribeTimelock.undelegate(MainnetAddresses.TRIBE_FEI_LABS_DEL);
tribeTimelock.undelegate(MainnetAddresses.TRIBE_FEI_LABS_DEL_2);
vm.stopPrank();
}

/// @notice Validate that timelock to burn is setup
Expand Down
2 changes: 1 addition & 1 deletion proposals/dao/old/phase_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const validate: ValidateUpgradeFunc = async (addresses, oldContracts, contracts,
expect(await contracts.uniswapLiquidityRemover.FEI_TRIBE_PAIR()).to.be.equal(addresses.feiTribePair);

// 3. Validate Fei Labs vesting timelock accepted beneficiary
expect(await contracts.feiLabsVestingTimelock.beneficiary()).to.be.equal(addresses.feiDAOTimelock);
expect(await contracts.tribeDAODelegationsTimelock.beneficiary()).to.be.equal(addresses.feiDAOTimelock);

// 4. Uniswap LP liquidity timelock should have no LP tokens or FEI or TRIBE
expect(await contracts.feiTribePair.balanceOf(addresses.uniswapFeiTribeLiquidityTimelock)).to.be.equal(0);
Expand Down
225 changes: 225 additions & 0 deletions proposals/dao/tip_123.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import {
DeployUpgradeFunc,
NamedAddresses,
NamedContracts,
PcvStats,
SetupUpgradeFunc,
TeardownUpgradeFunc,
ValidateUpgradeFunc
} from '@custom-types/types';
import { forceEth } from '@test/integration/setup/utils';
import { getImpersonatedSigner, time } from '@test/helpers';
import { BigNumber } from 'ethers';

/*

TIP_123

*/

let pcvStatsBefore: PcvStats;
let initialTotalTribeDelegation: BigNumber;

const fipNumber = 'tip_123';

// Do any deployments
// This should exclusively include new contract deployments
const deploy: DeployUpgradeFunc = async (deployAddress: string, addresses: NamedAddresses, logging: boolean) => {
// 1. Deploy DAOTimelockBurner, to burn admin of Fei and Rari DAO timelocks
const DAOTimelockBurnerFactory = await ethers.getContractFactory('DAOTimelockBurner');
const daoTimelockBurner = await DAOTimelockBurnerFactory.deploy();
console.log('DAO timelock burner deployed to: ', daoTimelockBurner.address);

const FeiTimelockBurnerFactory = await ethers.getContractFactory('FeiLinearTokenTimelockBurner');
// 2. Deploy deprecated Rari FEI timelock burner
const feiTimelockBurner1 = await FeiTimelockBurnerFactory.deploy(addresses.rariInfraFeiTimelock);
console.log('Deprecated Rari FEI timelock burner deployed to: ', feiTimelockBurner1.address);

// 3. Deploy deprecated Rari TRIBE timelock burner
const TribeTimelockedDelegatorBurnerFactory = await ethers.getContractFactory('TribeTimelockedDelegatorBurner');
const tribeTimelockBurner1 = await TribeTimelockedDelegatorBurnerFactory.deploy(addresses.rariInfraTribeTimelock);
console.log('Deprecated Rari TRIBE timelock burned deployed to: ', tribeTimelockBurner1.address);

// 4. Deploy Fei Labs burner
const tribeTimelockBurner2 = await TribeTimelockedDelegatorBurnerFactory.deploy(
addresses.tribeDAODelegationsTimelock
);
console.log('Tribe DAO delegations TRIBE burner deployed to: ', tribeTimelockBurner2.address);

return {
daoTimelockBurner,
feiTimelockBurner1,
tribeTimelockBurner1,
tribeTimelockBurner2
};
};

// Do any setup necessary for running the test.
// This could include setting up Hardhat to impersonate accounts,
// ensuring contracts have a specific state, etc.
const setup: SetupUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
pcvStatsBefore = await contracts.collateralizationOracle.pcvStats();
initialTotalTribeDelegation = await contracts.tribeDAODelegationsTimelock.totalDelegated();
};

// Tears down any changes made in setup() that need to be
// cleaned up before doing any validation checks.
const teardown: TeardownUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
console.log(`No actions to complete in teardown for fip${fipNumber}`);
};

// Run any validations required on the fip using mocha or console logging
// IE check balances, check state of contracts, etc.
const validate: ValidateUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {
// display pcvStats
console.log('----------------------------------------------------');
console.log(' pcvStatsBefore.protocolControlledValue [M]e18 ', Number(pcvStatsBefore.protocolControlledValue) / 1e24);
console.log(' pcvStatsBefore.userCirculatingFei [M]e18 ', Number(pcvStatsBefore.userCirculatingFei) / 1e24);
console.log(' pcvStatsBefore.protocolEquity [M]e18 ', Number(pcvStatsBefore.protocolEquity) / 1e24);
const pcvStatsAfter: PcvStats = await contracts.collateralizationOracle.pcvStats();
console.log('----------------------------------------------------');
console.log(' pcvStatsAfter.protocolControlledValue [M]e18 ', Number(pcvStatsAfter.protocolControlledValue) / 1e24);
console.log(' pcvStatsAfter.userCirculatingFei [M]e18 ', Number(pcvStatsAfter.userCirculatingFei) / 1e24);
console.log(' pcvStatsAfter.protocolEquity [M]e18 ', Number(pcvStatsAfter.protocolEquity) / 1e24);
console.log('----------------------------------------------------');
const pcvDiff = pcvStatsAfter.protocolControlledValue.sub(pcvStatsBefore.protocolControlledValue);
const cFeiDiff = pcvStatsAfter.userCirculatingFei.sub(pcvStatsBefore.userCirculatingFei);
const eqDiff = pcvStatsAfter.protocolEquity.sub(pcvStatsBefore.protocolEquity);
console.log(' PCV diff [M]e18 ', Number(pcvDiff) / 1e24);
console.log(' Circ FEI diff [M]e18 ', Number(cFeiDiff) / 1e24);
console.log(' Equity diff [M]e18 ', Number(eqDiff) / 1e24);
console.log('----------------------------------------------------');

// 0. Assert overcollaterised
expect(await contracts.collateralizationOracle.isOvercollateralized()).to.be.true;

// 1. Verify Fei DAO timelock admin burned
expect(await contracts.feiDAOTimelock.admin()).to.equal(addresses.daoTimelockBurner);
Copy link
Contributor

Choose a reason for hiding this comment

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

to be extra safe, would be good to verify that even succeeded votes on the DAO can't be executed. They probably can't even be queued.

Also good to verify that the number of addresses with governor role is 0, there is a method on the OZ AccessControlEnumerated which can check this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Verified num GOVERNORs is 0, and also verified num of addresses with other major roles is as expected

Copy link
Contributor Author

@thomas-waite thomas-waite Oct 10, 2022

Choose a reason for hiding this comment

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

Added test to verify can not queue successful proposals, and therefore can not execute


// Verify no addresses have GOVERN_ROLE, GUARDIAN_ROLE. One has PCV_CONTROLLER_ROLE, one has MINTER
expect(await contracts.core.getRoleMemberCount(ethers.utils.id('GOVERN_ROLE'))).to.equal(0);
expect(await contracts.core.getRoleMemberCount(ethers.utils.id('GUARDIAN_ROLE'))).to.equal(0);
expect(await contracts.core.getRoleMemberCount(ethers.utils.id('PCV_CONTROLLER_ROLE'))).to.equal(1);
expect(await contracts.core.getRoleMemberCount(ethers.utils.id('MINTER_ROLE'))).to.equal(1);

// 2. Verify Rari Fei deprecated timelock burned
expect(await contracts.rariInfraFeiTimelock.beneficiary()).to.equal(addresses.feiTimelockBurner1);

// 3. Verify Rari Tribe deprecated timelock burned
expect(await contracts.rariInfraTribeTimelock.beneficiary()).to.equal(addresses.tribeTimelockBurner1);

// Verify Fuse multisig does not have any delegated TRIBE
expect(await contracts.tribe.getCurrentVotes(addresses.fuseMultisig)).to.equal(0);

// 4. Verify Tribe DAO Delegations timelock burned
expect(await contracts.tribeDAODelegationsTimelock.beneficiary()).to.equal(addresses.tribeTimelockBurner2);

// 5. Verify Tribe minter set to zero address and inflation is the minimum of 0.01% (1 basis point)
expect(await contracts.tribe.minter()).to.equal(ethers.constants.AddressZero);
expect(await contracts.tribeMinter.annualMaxInflationBasisPoints()).to.equal(1);

// 6. Verify can not queue on DAO timelock
await verifyCanNotQueueProposals(contracts, addresses);

// 7. Verify proxyAdmin ownership renounced
expect(await contracts.proxyAdmin.owner()).to.equal(ethers.constants.AddressZero);

// 8. Verify can permissionlessly undelegated TRIBE from timelock via TRIBE timelock burner
const delegatee = '0x0d4ba14ca1e990654c6f9c7957b9b23f8a1429dc';
const expectedDelegateeDelegation = ethers.constants.WeiPerEther.mul(1_000_000);
expect(await contracts.tribeDAODelegationsTimelock.delegateAmount(delegatee)).to.equal(expectedDelegateeDelegation);

await contracts.tribeTimelockBurner2.undelegate(delegatee);

const finalTribeDelegated = await contracts.tribeDAODelegationsTimelock.totalDelegated();
const undelegatedAmount = initialTotalTribeDelegation.sub(finalTribeDelegated);

// Verify delegatee no longer has a delegation
expect(await contracts.tribeDAODelegationsTimelock.delegateAmount(delegatee)).to.equal(0);

// Verify total delegation decrease was equal to the delegates delegation
expect(undelegatedAmount).to.equal(expectedDelegateeDelegation);
thomas-waite marked this conversation as resolved.
Show resolved Hide resolved

// 9. Verify can permissionlessly burn FEI on Rari infra burner timelock
const initialFeiSupply = await contracts.fei.totalSupply();
const initialRariTimelockFei = await contracts.fei.balanceOf(addresses.rariInfraFeiTimelock);
await contracts.feiTimelockBurner1.burnFeiHeld();
const feiBurned = initialFeiSupply.sub(await contracts.fei.totalSupply());
const rariTimelockFeiLoss = initialRariTimelockFei.sub(await contracts.fei.balanceOf(addresses.rariInfraFeiTimelock));
expect(feiBurned).to.equal(rariTimelockFeiLoss);

// 10. Verify can permissionlessly sendTribeToTreasury() on Rari infra Tribe burner timelock
const initialCoreTreasury1 = await contracts.tribe.balanceOf(addresses.core);
const initialRariTimelockTribe1 = await contracts.tribe.balanceOf(addresses.rariInfraTribeTimelock);
await contracts.tribeTimelockBurner1.sendTribeToTreaury();
const rariTimelockTribeLoss1 = initialRariTimelockTribe1.sub(
await contracts.tribe.balanceOf(addresses.rariInfraTribeTimelock)
);
const coreTreasuryGain1 = (await contracts.tribe.balanceOf(addresses.core)).sub(initialCoreTreasury1);
expect(coreTreasuryGain1).to.equal(rariTimelockTribeLoss1);

// 11. Verify can permissionlessly sendTribeToTreasury() on Tribe DAO delegations burner timelock
// Undelegate TRIBE to make available
await contracts.tribeTimelockBurner2.undelegate('0xd046135ba00b0315ed4c3135206c87a7f4eb57d9');
await contracts.tribeTimelockBurner2.undelegate('0xc64ed730e030bdcb66e9b5703798bb4275a5a484');
await contracts.tribeTimelockBurner2.undelegate('0x114b8d7ab033e650003fa3fc72c5ba2d0fd18345');

const initialCoreTreasury2 = await contracts.tribe.balanceOf(addresses.core);
const initialDAOTimelockTribe2 = await contracts.tribe.balanceOf(addresses.tribeDAODelegationsTimelock);
await contracts.tribeTimelockBurner2.sendTribeToTreaury();
const daoTimelockTribeLoss2 = initialDAOTimelockTribe2.sub(
await contracts.tribe.balanceOf(addresses.tribeDAODelegationsTimelock)
);
const coreTreasuryGain2 = (await contracts.tribe.balanceOf(addresses.core)).sub(initialCoreTreasury2);
expect(coreTreasuryGain2).to.equal(daoTimelockTribeLoss2);
};

// Verify proposals can not be queued
const verifyCanNotQueueProposals = async (contracts: NamedContracts, addresses: NamedAddresses) => {
const feiDAO = contracts.feiDAO;

const targets = [feiDAO.address];
const values = [0];
const calldatas = [
'0x70b0f660000000000000000000000000000000000000000000000000000000000000000a' // set voting delay 10
];
const description: any[] = [];

const treasurySigner = await getImpersonatedSigner(addresses.core);
await forceEth(addresses.core);
await contracts.tribe.connect(treasurySigner).delegate(addresses.guardianMultisig);
const signer = await getImpersonatedSigner(addresses.guardianMultisig);

// Propose
// note ethers.js requires using this notation when two overloaded methods exist)
// https://docs.ethers.io/v5/migration/web3/#migration-from-web3-js--contracts--overloaded-functions
await feiDAO.connect(signer)['propose(address[],uint256[],bytes[],string)'](targets, values, calldatas, description);

const pid = await feiDAO.hashProposal(targets, values, calldatas, ethers.utils.keccak256(description));

await time.advanceBlock();

// vote
await feiDAO.connect(signer).castVote(pid, 1);

// advance to end of voting period
const endBlock = (await feiDAO.proposals(pid)).endBlock;
await time.advanceBlockTo(endBlock.toNumber());

expect(await contracts.feiDAO.state(pid)).to.equal(4); // SUCCEEDED state in ProposalState enum

// Attempt to queue on the timelock through the Fei DAO
// Queuing should fail as daoTimelockBurner is admin of timelock
await expect(
feiDAO['queue(address[],uint256[],bytes[],bytes32)'](
targets,
values,
calldatas,
ethers.utils.keccak256(description)
)
).to.be.revertedWith('Timelock: Call must come from admin.');
};

export { deploy, setup, teardown, validate };
2 changes: 1 addition & 1 deletion proposals/description/old/phase_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const phase_1: TemplatedProposalDescription = {
commands: [
// 1. Accept the beneficiary of Fei Labs vesting TRIBE timelock contract as the DAO timelock
{
target: 'feiLabsVestingTimelock',
target: 'tribeDAODelegationsTimelock',
values: '0',
method: 'acceptBeneficiary()',
arguments: (addresses) => [],
Expand Down
Loading