diff --git a/contracts/external/Unitroller.sol b/contracts/external/Unitroller.sol index f1c3fddb3..58c58b8d1 100644 --- a/contracts/external/Unitroller.sol +++ b/contracts/external/Unitroller.sol @@ -23,4 +23,6 @@ abstract contract Unitroller { function _setBorrowPaused(CToken cToken, bool borrowPaused) external virtual; function _acceptAdmin() external virtual returns (uint); function borrowGuardianPaused(address cToken) external view virtual returns(bool); + function comptrollerImplementation() external view virtual returns(address); + function rewardsDistributors(uint256 index) external view virtual returns(address); } \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 9ef667f51..9c935fe82 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -55,7 +55,7 @@ export default { forking: enableMainnetForking ? { url: `https://eth-mainnet.alchemyapi.io/v2/${mainnetAlchemyApiKey}`, - blockNumber: 13349710 + blockNumber: 13354217 } : undefined }, diff --git a/proposals/dao/feiRariStaking.ts b/proposals/dao/feiRariStaking.ts new file mode 100644 index 000000000..29cba81f8 --- /dev/null +++ b/proposals/dao/feiRariStaking.ts @@ -0,0 +1,87 @@ +import hre, { ethers, artifacts } from 'hardhat'; +import { expect } from 'chai'; +import { RunUpgradeFunc, SetupUpgradeFunc, TeardownUpgradeFunc } from '../../types/types'; +import { getImpersonatedSigner, increaseTime } from '@test/helpers'; +import { Tribe } from '@custom-types/contracts'; + +/* + +OA Proposal feiRariStaking + +Description: + +Steps: + 1 - Set pending Unitroller Impl + 2 - Become Unitroller Impl + 3 - Set Rewards Distributor + 4 - Add StakingTokenWraper + 5 - Initialize StakingTokenWrapper +*/ + +const setup: SetupUpgradeFunc = async (addresses, oldContracts, contracts, logging) => { + await increaseTime(5000000); +}; + +const run: RunUpgradeFunc = async (addresses, oldContracts, contracts, logging = false) => { + const { optimisticTimelock, rariPool8Comptroller, stakingTokenWrapperRari } = contracts; + const { tribalChiefOptimisticMultisig, tribalChief } = addresses; + + const oaSigner = await getImpersonatedSigner(tribalChiefOptimisticMultisig); + + await optimisticTimelock + .connect(oaSigner) + .execute( + rariPool8Comptroller.address, + 0, + '0xe992a041000000000000000000000000e16db319d9da7ce40b666dd2e365a4b8b3c18217', + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x3b8bd9479db83f492761601db65f2de2fd9fbac8304f45398fc31e3387d34d7e' + ); + + await optimisticTimelock + .connect(oaSigner) + .execute( + tribalChief, + 0, + '0x47a2dcae00000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000d81be1b9a7895c996704a8dda794bba4454eeb9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002710', + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x8302a6506ea19f4a4e1745122bf10dcafe35f0b2711299a53d0b78d1808cb70a' + ); + + await optimisticTimelock + .connect(oaSigner) + .executeBatch( + ['0xe16db319d9da7ce40b666dd2e365a4b8b3c18217', rariPool8Comptroller.address], + [0, 0], + [ + '0x1d504dc6000000000000000000000000c54172e34046c1653d1920d40333dd358c7a1af4', + '0xb9b5b15300000000000000000000000073f16f0c0cd1a078a54894974c5c054d8dc1a3d7' + ], + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1' + ); + + await stakingTokenWrapperRari.init(3); +}; + +const teardown: TeardownUpgradeFunc = async (addresses, oldContracts, contracts, logging) => {}; + +const validate = async (addresses, oldContracts, contracts) => { + const { rariPool8Comptroller, tribalChief, stakingTokenWrapperRari, tribe } = contracts; + const { rariRewardsDistributorDelegator } = addresses; + + expect(await rariPool8Comptroller.comptrollerImplementation()).to.be.equal( + '0xE16DB319d9dA7Ce40b666DD2E365a4b8B3C18217' + ); + expect(await rariPool8Comptroller.rewardsDistributors(0)).to.be.equal(rariRewardsDistributorDelegator); + expect(await tribalChief.stakedToken(3)).to.be.equal(stakingTokenWrapperRari.address); + expect(await tribalChief.numPools()).to.be.equal('4'); + expect(await tribalChief.totalAllocPoint()).to.be.equal('3100'); + expect((await tribalChief.poolInfo(3)).allocPoint).to.be.equal('1000'); + expect((await stakingTokenWrapperRari.pid()).toString()).to.be.equal('3'); + + await stakingTokenWrapperRari.harvest(); + expect((await tribe.balanceOf(rariRewardsDistributorDelegator)).toString()).to.be.not.equal('0'); +}; + +export { setup, run, teardown, validate }; diff --git a/test/helpers.ts b/test/helpers.ts index 683bb2235..f3194ab17 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -64,6 +64,33 @@ async function getImpersonatedSigner(address: string): Promise { return signer; } +async function increaseTime(amount: number) { + await hre.network.provider.request({ + method: 'evm_increaseTime', + params: [amount] + }); +} + +async function resetTime() { + await hre.network.provider.request({ + method: 'hardhat_reset', + params: [] + }); +} + +async function setNextBlockTimestamp(time: number) { + await hre.network.provider.request({ + method: 'evm_setNextBlockTimestamp', + params: [time] + }); +} + +async function mine() { + await hre.network.provider.request({ + method: 'evm_mine' + }); +} + async function getCore(): Promise { const { governorAddress, pcvControllerAddress, minterAddress, burnerAddress, guardianAddress } = await getAddresses(); @@ -109,9 +136,13 @@ export { balance, time, // functions + mine, getCore, getAddresses, + increaseTime, expectApprox, deployDevelopmentWeth, - getImpersonatedSigner + getImpersonatedSigner, + setNextBlockTimestamp, + resetTime }; diff --git a/test/integration/e2e.spec.ts b/test/integration/e2e.spec.ts index 132358e96..50d3c3551 100644 --- a/test/integration/e2e.spec.ts +++ b/test/integration/e2e.spec.ts @@ -57,6 +57,30 @@ describe('e2e', function () { }); describe('Fei DAO', function () { + it.skip('rollback succeeds', async function () { + const { feiDAO, timelock, governorAlphaBackup } = contracts; + const { multisig } = contractAddresses; + + const signer = await ethers.getSigner(multisig); + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [multisig] + }); + + const deadline = await feiDAO.ROLLBACK_DEADLINE(); + await feiDAO.connect(signer).__rollback(deadline); + + await time.increaseTo(deadline.toString()); + + await feiDAO.__executeRollback(); + + expect(await timelock.pendingAdmin()).to.be.equal(governorAlphaBackup.address); + + await governorAlphaBackup.connect(signer).__acceptAdmin(); + + expect(await timelock.admin()).to.be.equal(governorAlphaBackup.address); + }); + it('proposal succeeds', async function () { const feiDAO = contracts.feiDAO; @@ -114,30 +138,6 @@ describe('e2e', function () { expect((await feiDAO.votingDelay()).toString()).to.be.equal('10'); expect((await contracts.daiBondingCurve.duration()).toString()).to.be.equal('11'); }); - - it('rollback succeeds', async function () { - const { feiDAO, timelock, governorAlphaBackup } = contracts; - const { multisig } = contractAddresses; - - const signer = await ethers.getSigner(multisig); - await hre.network.provider.request({ - method: 'hardhat_impersonateAccount', - params: [multisig] - }); - - const deadline = await feiDAO.ROLLBACK_DEADLINE(); - await feiDAO.connect(signer).__rollback(deadline); - - await time.increaseTo(deadline.toString()); - - await feiDAO.__executeRollback(); - - expect(await timelock.pendingAdmin()).to.be.equal(governorAlphaBackup.address); - - await governorAlphaBackup.connect(signer).__acceptAdmin(); - - expect(await timelock.admin()).to.be.equal(governorAlphaBackup.address); - }); }); describe('PCV Equity Minter + LBP', async function () { it('mints appropriate amount and swaps', async function () { diff --git a/test/integration/proposals_config.json b/test/integration/proposals_config.json index 645a8a440..5cfb2be9b 100644 --- a/test/integration/proposals_config.json +++ b/test/integration/proposals_config.json @@ -1,4 +1,9 @@ { + "feiRariStaking" : { + "deploy" : false, + "exec" : false, + "proposerAddress" : "0xb81cf4981Ef648aaA73F07a18B03970f04d5D8bF" + }, "fip_30" : { "deploy" : false, "exec" : true, diff --git a/test/unit/dao/FeiDao.test.ts b/test/unit/dao/FeiDao.test.ts index 4757df8f4..eceae8002 100644 --- a/test/unit/dao/FeiDao.test.ts +++ b/test/unit/dao/FeiDao.test.ts @@ -120,7 +120,7 @@ describe('FeiDAO', function () { it('not from admin reverts', async function () { await expectRevert( feiDAO.connect(impersonatedSigners[governorAddress]).__rollback('10'), - 'FeiDAO: caller not admin' + 'FeiDAO: caller not guardian' ); });