Skip to content

Commit

Permalink
Merge branch 'polygon' into master-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
alsco77 committed Apr 27, 2021
2 parents 4cf4345 + 60941e9 commit 019a0fa
Show file tree
Hide file tree
Showing 98 changed files with 251,012 additions and 388,085 deletions.
7 changes: 7 additions & 0 deletions .commitlintrc.js
@@ -0,0 +1,7 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
"scope-case": [2, 'always', 'lower-case'],
"subject-case": [2, 'always', 'sentence-case'],
}
}
1 change: 1 addition & 0 deletions .husky/.gitignore
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions .husky/commit-msg
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn commitlint --edit "$1"
4 changes: 4 additions & 0 deletions .husky/pre-commit
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint:fix
52 changes: 12 additions & 40 deletions contracts/feeders/InterestValidator.sol
Expand Up @@ -5,6 +5,7 @@ import { IFeederPool } from "../interfaces/IFeederPool.sol";
import { PausableModule } from "../shared/PausableModule.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { YieldValidator } from "../shared/YieldValidator.sol";

/**
* @title InterestValidator
Expand All @@ -25,11 +26,6 @@ contract InterestValidator is PausableModule {
);
event GovFeeCollected(address indexed feederPool, address mAsset, uint256 amount);

// Utils to help keep interest under check
uint256 private constant SECONDS_IN_YEAR = 365 days;
// Theoretical cap on APY to avoid excess inflation
uint256 private constant MAX_APY = 15e18;

mapping(address => uint256) public lastBatchCollected;

constructor(address _nexus) PausableModule(_nexus) {}
Expand Down Expand Up @@ -60,7 +56,11 @@ contract InterestValidator is PausableModule {
if (interestCollected > 0) {
// Validate APY
uint256 apy =
_validateCollection(totalSupply, interestCollected, timeSincePreviousBatch);
YieldValidator.validateCollection(
totalSupply,
interestCollected,
timeSincePreviousBatch
);

emit InterestCollected(feeder, interestCollected, totalSupply, apy);
} else {
Expand All @@ -87,42 +87,14 @@ contract InterestValidator is PausableModule {
if (fpTokenBal > 0) {
address mAsset = IFeederPool(fPool).mAsset();
uint256 outputAmt =
IFeederPool(fPool).redeem(mAsset, fpTokenBal, (fpTokenBal * 7) / 10, savingsManager);
IFeederPool(fPool).redeem(
mAsset,
fpTokenBal,
(fpTokenBal * 7) / 10,
savingsManager
);
emit GovFeeCollected(fPool, mAsset, outputAmt);
}
}
}

/**
* @dev Validates that an interest collection does not exceed a maximum APY. If last collection
* was under 30 mins ago, simply check it does not exceed 10bps
* @param _newSupply New total supply of the mAsset
* @param _interest Increase in total supply since last collection
* @param _timeSinceLastCollection Seconds since last collection
*/
function _validateCollection(
uint256 _newSupply,
uint256 _interest,
uint256 _timeSinceLastCollection
) internal pure returns (uint256 extrapolatedAPY) {
// Percentage increase in total supply
// e.g. (1e20 * 1e18) / 1e24 = 1e14 (or a 0.01% increase)
// e.g. (5e18 * 1e18) / 1.2e24 = 4.1667e12
// e.g. (1e19 * 1e18) / 1e21 = 1e16
uint256 oldSupply = _newSupply - _interest;
uint256 percentageIncrease = (_interest * 1e18) / oldSupply;

// If over 30 mins, extrapolate APY
// e.g. day: (86400 * 1e18) / 3.154e7 = 2.74..e15
// e.g. 30 mins: (1800 * 1e18) / 3.154e7 = 5.7..e13
// e.g. epoch: (1593596907 * 1e18) / 3.154e7 = 50.4..e18
uint256 yearsSinceLastCollection = (_timeSinceLastCollection * 1e18) / SECONDS_IN_YEAR;

// e.g. 0.01% (1e14 * 1e18) / 2.74..e15 = 3.65e16 or 3.65% apr
// e.g. (4.1667e12 * 1e18) / 5.7..e13 = 7.1e16 or 7.1% apr
// e.g. (1e16 * 1e18) / 50e18 = 2e14
extrapolatedAPY = (percentageIncrease * 1e18) / yearsSinceLastCollection;

require(extrapolatedAPY < MAX_APY, "Interest protected from inflating past maxAPY");
}
}
69 changes: 69 additions & 0 deletions contracts/governance/ClaimableGovernor.sol
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.2;

import { Governable } from "./Governable.sol";

/**
* @title ClaimableGovernor
* @author mStable
* @notice 2 way handshake for Governance transfer
* @dev Overrides the public functions in Governable to provide
* a second step of validation.
* VERSION: 1.1
* DATE: 2021-04-15
*/
contract ClaimableGovernor is Governable {
event GovernorChangeClaimed(address indexed proposedGovernor);
event GovernorChangeCancelled(address indexed governor, address indexed proposed);
event GovernorChangeRequested(address indexed governor, address indexed proposed);

address public proposedGovernor = address(0);

/**
* @dev Throws if called by any account other than the Proposed Governor.
*/
modifier onlyProposedGovernor() {
require(msg.sender == proposedGovernor, "Sender is not proposed governor");
_;
}

constructor(address _governorAddr) {
_changeGovernor(_governorAddr);
}

// @override
function changeGovernor(address) external view override onlyGovernor {
revert("Direct change not allowed");
}

/**
* @dev Current Governor request to proposes a new Governor
* @param _proposedGovernor Address of the proposed Governor
*/
function requestGovernorChange(address _proposedGovernor) public virtual onlyGovernor {
require(_proposedGovernor != address(0), "Proposed governor is address(0)");
require(proposedGovernor == address(0), "Proposed governor already set");

proposedGovernor = _proposedGovernor;
emit GovernorChangeRequested(governor(), _proposedGovernor);
}

/**
* @dev Current Governor cancel Governor change request
*/
function cancelGovernorChange() public virtual onlyGovernor {
require(proposedGovernor != address(0), "Proposed Governor not set");

emit GovernorChangeCancelled(governor(), proposedGovernor);
proposedGovernor = address(0);
}

/**
* @dev Proposed Governor can claim governance ownership
*/
function claimGovernorChange() public virtual onlyProposedGovernor {
_changeGovernor(proposedGovernor);
emit GovernorChangeClaimed(proposedGovernor);
proposedGovernor = address(0);
}
}
54 changes: 54 additions & 0 deletions contracts/governance/DelayedClaimableGovernor.sol
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.2;

import { ClaimableGovernor } from "./ClaimableGovernor.sol";

/**
* @title DelayedClaimableGovernor
* @author mStable
* @notice Current Governor can initiate governance change request.
* After a defined delay, proposed Governor can claim governance
* ownership.
* VERSION: 1.1
* DATE: 2021-04-15
*/
contract DelayedClaimableGovernor is ClaimableGovernor {
uint256 public delay = 0;
uint256 public requestTime = 0;

/**
* @dev Initializes the contract with given delay
* @param _governorAddr Initial governor
* @param _delay Delay in seconds for 2 way handshake
*/
constructor(address _governorAddr, uint256 _delay) ClaimableGovernor(_governorAddr) {
require(_delay > 0, "Delay must be greater than zero");
delay = _delay;
}

/**
* @dev Requests change of governor and logs request time
* @param _proposedGovernor Address of the new governor
*/
function requestGovernorChange(address _proposedGovernor) public override onlyGovernor {
requestTime = block.timestamp;
super.requestGovernorChange(_proposedGovernor);
}

/**
* @dev Cancels an outstanding governor change request by resetting request time
*/
function cancelGovernorChange() public override onlyGovernor {
requestTime = 0;
super.cancelGovernorChange();
}

/**
* @dev Proposed governor claims new position, callable after time elapsed
*/
function claimGovernorChange() public override onlyProposedGovernor {
require(block.timestamp >= (requestTime + delay), "Delay not over");
super.claimGovernorChange();
requestTime = 0;
}
}
68 changes: 68 additions & 0 deletions contracts/governance/Governable.sol
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.2;

/**
* @title Governable
* @author mStable
* @notice Simple contract implementing an Ownable pattern.
* @dev Derives from V2.3.0 @openzeppelin/contracts/ownership/Ownable.sol
* Modified to have custom name and features
* - Removed `renounceOwnership`
* - Changes `_owner` to `_governor`
* VERSION: 1.1
* DATE: 2021-04-15
*/
contract Governable {
event GovernorChanged(address indexed previousGovernor, address indexed newGovernor);

address private _governor;

/**
* @dev Initializes the contract setting the deployer as the initial Governor.
*/
constructor() {
_governor = msg.sender;
emit GovernorChanged(address(0), _governor);
}

/**
* @dev Returns the address of the current Governor.
*/
function governor() public view virtual returns (address) {
return _governor;
}

/**
* @dev Throws if called by any account other than the Governor.
*/
modifier onlyGovernor() {
require(isGovernor(), "GOV: caller is not the Governor");
_;
}

/**
* @dev Returns true if the caller is the current Governor.
*/
function isGovernor() public view returns (bool) {
return msg.sender == _governor;
}

/**
* @dev Transfers Governance of the contract to a new account (`newGovernor`).
* Can only be called by the current Governor.
* @param _newGovernor Address of the new Governor
*/
function changeGovernor(address _newGovernor) external virtual onlyGovernor {
_changeGovernor(_newGovernor);
}

/**
* @dev Change Governance of the contract to a new account (`newGovernor`).
* @param _newGovernor Address of the new Governor
*/
function _changeGovernor(address _newGovernor) internal {
require(_newGovernor != address(0), "GOV: new Governor is address(0)");
emit GovernorChanged(_governor, _newGovernor);
_governor = _newGovernor;
}
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IEjector.sol
Expand Up @@ -8,5 +8,5 @@ pragma solidity 0.8.2;
interface IEjector {
function ejectMany(address[] calldata _users) external;

function votingLockup() view external returns (address);
function votingLockup() external view returns (address);
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IInvariantValidator.sol
Expand Up @@ -46,7 +46,7 @@ abstract contract IInvariantValidator {

function computePrice(BassetData[] memory _bAssets, InvariantConfig memory _config)
public
virtual
pure
virtual
returns (uint256 price, uint256 k);
}
2 changes: 1 addition & 1 deletion contracts/interfaces/ISavingsManager.sol
Expand Up @@ -18,5 +18,5 @@ interface ISavingsManager {
function collectAndDistributeInterest(address _mAsset) external;

/** @dev getter for public lastBatchCollected mapping */
function lastBatchCollected(address _mAsset) view external returns (uint256);
function lastBatchCollected(address _mAsset) external view returns (uint256);
}

0 comments on commit 019a0fa

Please sign in to comment.