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

FIP-60 - Fuse guardian #430

Merged
merged 6 commits into from
Jan 5, 2022
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
16 changes: 15 additions & 1 deletion contracts/external/Unitroller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,33 @@ abstract contract Unitroller {
}

address public admin;
address public borrowCapGuardian;
address public pauseGuardian;

address public oracle;
address public pendingAdmin;
uint public closeFactorMantissa;
uint public liquidationIncentiveMantissa;
mapping(address => Market) public markets;
mapping(address => address) public cTokensByUnderlying;
mapping(address => uint) public supplyCaps;

function _setPendingAdmin(address newPendingAdmin) public virtual returns (uint);

function _setBorrowCapGuardian(address newBorrowCapGuardian) public virtual;
function _setMarketSupplyCaps(CToken[] calldata cTokens, uint[] calldata newSupplyCaps) external virtual;
function _setMarketBorrowCaps(CToken[] calldata cTokens, uint[] calldata newBorrowCaps) external virtual;

function _setPauseGuardian(address newPauseGuardian) public virtual returns (uint);
function _setMintPaused(CToken cToken, bool state) public virtual returns (bool);
function _setBorrowPaused(CToken cToken, bool borrowPaused) public virtual returns (bool);
function _setTransferPaused(bool state) public virtual returns (bool);
function _setSeizePaused(bool state) public virtual returns (bool);

function _setPriceOracle(address newOracle) external virtual returns (uint256);
function _setCloseFactor(uint newCloseFactorMantissa) external virtual returns (uint256);
function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external virtual returns (uint);
function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) public virtual returns (uint256);
function _setBorrowPaused(CToken cToken, bool borrowPaused) external virtual;
function _acceptAdmin() external virtual returns (uint);
function _deployMarket(bool isCEther, bytes calldata constructionData, uint256 collateralFactorMantissa) external virtual returns (uint);
function borrowGuardianPaused(address cToken) external view virtual returns(bool);
Expand Down
125 changes: 125 additions & 0 deletions contracts/feirari/FuseGuardian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
pragma solidity ^0.8.0;

import "../refs/CoreRef.sol";
import "../external/Unitroller.sol";

/// @title a Fuse pause and borrow cap guardian used to expand access control to more Fei roles
/// @author joeysantoro
contract FuseGuardian is CoreRef {

/// @notice the fuse comptroller
Unitroller public immutable comptroller;

/// @param _core address of core contract
/// @param _comptroller the fuse comptroller
constructor(
address _core,
Unitroller _comptroller
) CoreRef(_core) {
comptroller = _comptroller;
/// @notice The reason we are reusing the tribal chief admin role is it consolidates control in the OA,
/// and means we don't have to do another governance action to create this role in core
_setContractAdminRole(keccak256("TRIBAL_CHIEF_ADMIN_ROLE"));
}

// ************ BORROW GUARDIAN FUNCTIONS ************
/**
* @notice Set the given supply caps for the given cToken markets. Supplying that brings total underlying supply to or above supply cap will revert.
* @dev Admin or borrowCapGuardian function to set the supply caps. A supply cap of 0 corresponds to unlimited supplying.
* @param cTokens The addresses of the markets (tokens) to change the supply caps for
* @param newSupplyCaps The new supply cap values in underlying to be set. A value of 0 corresponds to unlimited supplying.
*/
function _setMarketSupplyCaps(CToken[] memory cTokens, uint[] calldata newSupplyCaps) external isGovernorOrGuardianOrAdmin {
_setMarketSupplyCapsInternal(cTokens, newSupplyCaps);
}

function _setMarketSupplyCapsByUnderlying(address[] calldata underlyings, uint[] calldata newSupplyCaps) external isGovernorOrGuardianOrAdmin {
_setMarketSupplyCapsInternal(_underlyingToCTokens(underlyings), newSupplyCaps);
}

function _setMarketSupplyCapsInternal(CToken[] memory cTokens, uint[] calldata newSupplyCaps) internal {
comptroller._setMarketSupplyCaps(cTokens, newSupplyCaps);
}

function _underlyingToCTokens(address[] calldata underlyings) internal view returns (CToken[] memory) {
CToken[] memory cTokens = new CToken[](underlyings.length);
for (uint256 i = 0; i < underlyings.length; i++) {
Joeysantoro marked this conversation as resolved.
Show resolved Hide resolved
address cToken = comptroller.cTokensByUnderlying(underlyings[i]);
require(cToken != address(0), "cToken doesn't exist");
cTokens[i] = CToken(cToken);
}
return cTokens;
Joeysantoro marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert.
* @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing.
* @param cTokens The addresses of the markets (tokens) to change the borrow caps for
* @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing.
*/
function _setMarketBorrowCaps(CToken[] memory cTokens, uint[] calldata newBorrowCaps) external isGovernorOrGuardianOrAdmin {
_setMarketBorrowCapsInternal(cTokens, newBorrowCaps);
}

function _setMarketBorrowCapsInternal(CToken[] memory cTokens, uint[] calldata newBorrowCaps) internal {
comptroller._setMarketBorrowCaps(cTokens, newBorrowCaps);
}

function _setMarketBorrowCapsByUnderlying(address[] calldata underlyings, uint[] calldata newBorrowCaps) external isGovernorOrGuardianOrAdmin {
_setMarketBorrowCapsInternal(_underlyingToCTokens(underlyings), newBorrowCaps);
}

/**
* @notice Admin function to change the Borrow Cap Guardian
* @param newBorrowCapGuardian The address of the new Borrow Cap Guardian
*/
function _setBorrowCapGuardian(address newBorrowCapGuardian) external onlyGovernor {
comptroller._setBorrowCapGuardian(newBorrowCapGuardian);
}

// ************ PAUSE GUARDIAN FUNCTIONS ************
/**
* @notice Admin function to change the Pause Guardian
* @param newPauseGuardian The address of the new Pause Guardian
* @return uint 0=success, otherwise a failure. (See enum Error for details)
*/
function _setPauseGuardian(address newPauseGuardian) external onlyGovernor returns (uint) {
return comptroller._setPauseGuardian(newPauseGuardian);
}

function _setMintPausedByUnderlying(address underlying, bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
address cToken = comptroller.cTokensByUnderlying(underlying);
require(cToken != address(0), "cToken doesn't exist");
_setMintPausedInternal(CToken(cToken), state);
}

function _setMintPaused(CToken cToken, bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
return _setMintPausedInternal(cToken, state);
}

function _setMintPausedInternal(CToken cToken, bool state) internal returns (bool) {
return comptroller._setMintPaused(cToken, state);
}

function _setBorrowPausedByUnderlying(address underlying, bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
address cToken = comptroller.cTokensByUnderlying(underlying);
require(cToken != address(0), "cToken doesn't exist");
return _setBorrowPausedInternal(CToken(cToken), state);
}

function _setBorrowPausedInternal(CToken cToken, bool state) internal returns (bool) {
return comptroller._setBorrowPaused(cToken, state);
}

function _setBorrowPaused(CToken cToken, bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
_setBorrowPausedInternal(CToken(cToken), state);
}

function _setTransferPaused(bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
return comptroller._setTransferPaused(state);
}

function _setSeizePaused(bool state) external isGovernorOrGuardianOrAdmin returns (bool) {
return comptroller._setSeizePaused(state);
}
}
39 changes: 35 additions & 4 deletions proposals/dao/fip_60.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ DEPLOY ACTIONS:
5. Deploy AutoRewardsDistributor d3
6. Deploy AutoRewardsDistributor FEI-3Crv
7. Deploy AutoRewardsDistributor Gelato
8. Deploy Fuse Pause Guardian

OA ACTIONS:
1. Add cTokens
Expand Down Expand Up @@ -116,12 +117,20 @@ export const deploy: DeployUpgradeFunc = async (deployAddress, addresses, loggin

logging && console.log('fei3CrvAutoRewardsDistributor: ', fei3CrvAutoRewardsDistributor.address);

const pauseFactory = await ethers.getContractFactory('FuseGuardian');
const fuseGuardian = await pauseFactory.deploy(core, rariPool8Comptroller);

await fuseGuardian.deployed();

logging && console.log('fuseGuardian: ', fuseGuardian.address);

return {
tribalChiefSyncExtension,
d3StakingTokenWrapper,
fei3CrvStakingtokenWrapper,
d3AutoRewardsDistributor,
fei3CrvAutoRewardsDistributor
fei3CrvAutoRewardsDistributor,
fuseGuardian
} as NamedContracts;
};

Expand All @@ -148,14 +157,37 @@ export const validate: ValidateUpgradeFunc = async (addresses, oldContracts, con
fei3CrvStakingtokenWrapper,
d3AutoRewardsDistributor,
fei3CrvAutoRewardsDistributor,
rariRewardsDistributorDelegator
rariRewardsDistributorDelegator,
rariPool8Comptroller
} = contracts;
const comptroller = contracts.rariPool8Comptroller;

const d3Ctoken = await comptroller.cTokensByUnderlying(addresses.curveD3pool);
expect(d3Ctoken).to.not.be.equal(ethers.constants.AddressZero);

const fei3CrvCtoken = await comptroller.cTokensByUnderlying(addresses.curve3Metapool);
expect(fei3CrvCtoken).to.not.be.equal(ethers.constants.AddressZero);

expect(d3Ctoken).to.not.be.equal(ethers.constants.AddressZero);
// Ctoken configs
// supply cap
expect(await rariPool8Comptroller.supplyCaps(d3Ctoken)).to.be.equal(ethers.constants.WeiPerEther.mul(25_000_000)); // 25 M
expect(await rariPool8Comptroller.supplyCaps(fei3CrvCtoken)).to.be.equal(
ethers.constants.WeiPerEther.mul(25_000_000)
); // 25 M

// borrow paused
expect(await rariPool8Comptroller.borrowGuardianPaused(d3Ctoken)).to.be.true;
expect(await rariPool8Comptroller.borrowGuardianPaused(fei3CrvCtoken)).to.be.true;

// LTV
expect((await rariPool8Comptroller.markets(d3Ctoken)).collateralFactorMantissa).to.be.equal(
ethers.constants.WeiPerEther.mul(60).div(100)
);
expect((await rariPool8Comptroller.markets(fei3CrvCtoken)).collateralFactorMantissa).to.be.equal(
ethers.constants.WeiPerEther.mul(60).div(100)
);

// Rewards configs
expect(d3Ctoken).to.be.equal(await d3AutoRewardsDistributor.cTokenAddress());
expect(await d3StakingTokenWrapper.pid()).to.be.equal(await d3AutoRewardsDistributor.tribalChiefRewardIndex());
expect((await tribalChief.poolInfo(await d3StakingTokenWrapper.pid())).allocPoint).to.be.equal(250);
Expand All @@ -164,7 +196,6 @@ export const validate: ValidateUpgradeFunc = async (addresses, oldContracts, con
console.log(`d3 reward speed: ${d3RewardSpeed[0]}`);
expect(d3RewardSpeed[0]).to.be.equal(await rariRewardsDistributorDelegator.compSupplySpeeds(d3Ctoken));

expect(fei3CrvCtoken).to.not.be.equal(ethers.constants.AddressZero);
expect(fei3CrvCtoken).to.be.equal(await fei3CrvAutoRewardsDistributor.cTokenAddress());
expect(await fei3CrvStakingtokenWrapper.pid()).to.be.equal(
await fei3CrvAutoRewardsDistributor.tribalChiefRewardIndex()
Expand Down
38 changes: 38 additions & 0 deletions proposals/description/fip_60.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,44 @@ const fip_60: ProposalDescription = {
],
description: 'Add FEI-3Crv to FeiRari'
},
{
target: 'rariPool8Comptroller',
values: '0',
method: '_setPauseGuardian(address)',
arguments: ['{fuseGuardian}'],
description: 'Set Fuse pause guardian'
},
{
target: 'fuseGuardian',
values: '0',
method: '_setBorrowPausedByUnderlying(address,bool)',
arguments: ['{curveD3pool}', true],
description: 'Set d3 borrow paused'
},
{
target: 'fuseGuardian',
values: '0',
method: '_setBorrowPausedByUnderlying(address,bool)',
arguments: ['{curve3Metapool}', true],
description: 'Set Fei-3Crv borrow paused'
},
{
target: 'rariPool8Comptroller',
values: '0',
method: '_setBorrowCapGuardian(address)',
arguments: ['{fuseGuardian}'],
description: 'Set Fuse borrow cap guardian'
},
{
target: 'fuseGuardian',
values: '0',
method: '_setMarketSupplyCapsByUnderlying(address[],uint256[])',
arguments: [
['{curveD3pool}', '{curve3Metapool}'],
['25000000000000000000000000', '25000000000000000000000000']
],
description: 'Set Fuse supply caps'
},
{
target: 'tribalChief',
values: '0',
Expand Down