-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7a7b0f3
commit 76272bc
Showing
11 changed files
with
388 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,104 +11,115 @@ import { | |
import {IIrm} from "./interfaces/IIrm.sol"; | ||
import {IERC20} from "./interfaces/IERC20.sol"; | ||
import {IOracle} from "./interfaces/IOracle.sol"; | ||
import {Id, Market, Signature, IBlue} from "./interfaces/IBlue.sol"; | ||
import {Id, Market, Signature, IBlue, IFlashLender} from "./interfaces/IBlue.sol"; | ||
|
||
import {Errors} from "./libraries/Errors.sol"; | ||
import {SharesMath} from "./libraries/SharesMath.sol"; | ||
import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; | ||
import {MarketLib} from "./libraries/MarketLib.sol"; | ||
import {SharesMath} from "./libraries/SharesMath.sol"; | ||
import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; | ||
import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; | ||
|
||
/// @dev The maximum fee a market can have (25%). | ||
uint256 constant MAX_FEE = 0.25e18; | ||
/// @dev The alpha parameter used to compute the incentive during a liquidation. | ||
uint256 constant ALPHA = 0.5e18; | ||
|
||
/// @dev The EIP-712 typeHash for EIP712Domain. | ||
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); | ||
|
||
/// @dev The EIP-712 typeHash for Authorization. | ||
bytes32 constant AUTHORIZATION_TYPEHASH = | ||
keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)"); | ||
|
||
/// @title Blue | ||
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
/// @notice The Blue contract. | ||
contract Blue is IBlue { | ||
using SharesMath for uint256; | ||
using FixedPointMathLib for uint256; | ||
using SafeTransferLib for IERC20; | ||
using SharesMath for uint256; | ||
using MarketLib for Market; | ||
|
||
// Immutables. | ||
/* IMMUTABLES */ | ||
|
||
/// @inheritdoc IBlue | ||
bytes32 public immutable DOMAIN_SEPARATOR; | ||
|
||
// Storage. | ||
/* STORAGE */ | ||
|
||
// Owner. | ||
/// @inheritdoc IBlue | ||
address public owner; | ||
// Fee recipient. | ||
/// @inheritdoc IBlue | ||
address public feeRecipient; | ||
// User' supply balances. | ||
/// @inheritdoc IBlue | ||
mapping(Id => mapping(address => uint256)) public supplyShares; | ||
// User' borrow balances. | ||
/// @inheritdoc IBlue | ||
mapping(Id => mapping(address => uint256)) public borrowShares; | ||
// User' collateral balance. | ||
/// @inheritdoc IBlue | ||
mapping(Id => mapping(address => uint256)) public collateral; | ||
// Market total supply. | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public totalSupply; | ||
// Market total supply shares. | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public totalSupplyShares; | ||
// Market total borrow. | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public totalBorrow; | ||
// Market total borrow shares. | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public totalBorrowShares; | ||
// Interests last update (used to check if a market has been created). | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public lastUpdate; | ||
// Fee. | ||
/// @inheritdoc IBlue | ||
mapping(Id => uint256) public fee; | ||
// Enabled IRMs. | ||
/// @inheritdoc IBlue | ||
mapping(address => bool) public isIrmEnabled; | ||
// Enabled LLTVs. | ||
/// @inheritdoc IBlue | ||
mapping(uint256 => bool) public isLltvEnabled; | ||
// User's authorizations. Note that by default, msg.sender is authorized by themself. | ||
/// @inheritdoc IBlue | ||
mapping(address => mapping(address => bool)) public isAuthorized; | ||
// User's nonces. Used to prevent replay attacks with EIP-712 signatures. | ||
/// @inheritdoc IBlue | ||
mapping(address => uint256) public nonce; | ||
|
||
// Constructor. | ||
/* CONSTRUCTOR */ | ||
|
||
/// @notice Initializes the contract. | ||
/// @param newOwner The new owner of the contract. | ||
constructor(address newOwner) { | ||
owner = newOwner; | ||
|
||
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256("Blue"), block.chainid, address(this))); | ||
} | ||
|
||
// Modifiers. | ||
/* MODIFIERS */ | ||
|
||
/// @notice Reverts if the caller is not the owner. | ||
modifier onlyOwner() { | ||
require(msg.sender == owner, Errors.NOT_OWNER); | ||
_; | ||
} | ||
|
||
// Only owner functions. | ||
/* ADMIN FUNCTIONS */ | ||
|
||
/// @inheritdoc IBlue | ||
function setOwner(address newOwner) external onlyOwner { | ||
owner = newOwner; | ||
|
||
emit SetOwner(newOwner); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function enableIrm(address irm) external onlyOwner { | ||
isIrmEnabled[irm] = true; | ||
|
||
emit EnableIrm(address(irm)); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function enableLltv(uint256 lltv) external onlyOwner { | ||
require(lltv < FixedPointMathLib.WAD, Errors.LLTV_TOO_HIGH); | ||
isLltvEnabled[lltv] = true; | ||
|
||
emit EnableLltv(lltv); | ||
} | ||
|
||
/// @notice It is the owner's responsibility to ensure a fee recipient is set before setting a non-zero fee. | ||
/// @inheritdoc IBlue | ||
function setFee(Market memory market, uint256 newFee) external onlyOwner { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -118,14 +129,16 @@ contract Blue is IBlue { | |
emit SetFee(id, newFee); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function setFeeRecipient(address recipient) external onlyOwner { | ||
feeRecipient = recipient; | ||
|
||
emit SetFeeRecipient(recipient); | ||
} | ||
|
||
// Markets management. | ||
/* MARKET CREATION */ | ||
|
||
/// @inheritdoc IBlue | ||
function createMarket(Market memory market) external { | ||
Id id = market.id(); | ||
require(isIrmEnabled[market.irm], Errors.IRM_NOT_ENABLED); | ||
|
@@ -136,8 +149,9 @@ contract Blue is IBlue { | |
emit CreateMarket(id, market); | ||
} | ||
|
||
// Supply management. | ||
/* SUPPLY MANAGEMENT */ | ||
|
||
/// @inheritdoc IBlue | ||
function supply(Market memory market, uint256 amount, address onBehalf, bytes calldata data) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -159,6 +173,7 @@ contract Blue is IBlue { | |
IERC20(market.borrowableAsset).safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function withdraw(Market memory market, uint256 shares, address onBehalf, address receiver) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -182,8 +197,9 @@ contract Blue is IBlue { | |
IERC20(market.borrowableAsset).safeTransfer(receiver, amount); | ||
} | ||
|
||
// Borrow management. | ||
/* BORROW MANAGEMENT */ | ||
|
||
/// @inheritdoc IBlue | ||
function borrow(Market memory market, uint256 amount, address onBehalf, address receiver) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -208,6 +224,7 @@ contract Blue is IBlue { | |
IERC20(market.borrowableAsset).safeTransfer(receiver, amount); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function repay(Market memory market, uint256 shares, address onBehalf, bytes calldata data) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -229,9 +246,9 @@ contract Blue is IBlue { | |
IERC20(market.borrowableAsset).safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
// Collateral management. | ||
/* COLLATERAL MANAGEMENT */ | ||
|
||
/// @dev Don't accrue interests because it's not required and it saves gas. | ||
/// @inheritdoc IBlue | ||
function supplyCollateral(Market memory market, uint256 amount, address onBehalf, bytes calldata data) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -249,6 +266,7 @@ contract Blue is IBlue { | |
IERC20(market.collateralAsset).safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function withdrawCollateral(Market memory market, uint256 amount, address onBehalf, address receiver) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -268,8 +286,9 @@ contract Blue is IBlue { | |
IERC20(market.collateralAsset).safeTransfer(receiver, amount); | ||
} | ||
|
||
// Liquidation. | ||
/* LIQUIDATION */ | ||
|
||
/// @inheritdoc IBlue | ||
function liquidate(Market memory market, address borrower, uint256 seized, bytes calldata data) external { | ||
Id id = market.id(); | ||
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); | ||
|
@@ -314,8 +333,9 @@ contract Blue is IBlue { | |
IERC20(market.borrowableAsset).safeTransferFrom(msg.sender, address(this), repaid); | ||
} | ||
|
||
// Flash Loans. | ||
/* FLASH LOANS */ | ||
|
||
/// @inheritdoc IFlashLender | ||
function flashLoan(address token, uint256 amount, bytes calldata data) external { | ||
IERC20(token).safeTransfer(msg.sender, amount); | ||
|
||
|
@@ -326,9 +346,9 @@ contract Blue is IBlue { | |
IERC20(token).safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
// Authorizations. | ||
/* AUTHORIZATION */ | ||
|
||
/// @dev The signature is malleable, but it has no impact on the security here. | ||
/// @inheritdoc IBlue | ||
function setAuthorization( | ||
address authorizer, | ||
address authorized, | ||
|
@@ -353,18 +373,23 @@ contract Blue is IBlue { | |
emit SetAuthorization(msg.sender, authorizer, authorized, newIsAuthorized); | ||
} | ||
|
||
/// @inheritdoc IBlue | ||
function setAuthorization(address authorized, bool newIsAuthorized) external { | ||
isAuthorized[msg.sender][authorized] = newIsAuthorized; | ||
|
||
emit SetAuthorization(msg.sender, msg.sender, authorized, newIsAuthorized); | ||
} | ||
|
||
/// @dev Returns whether `user` is authorized to manage `msg.sender`'s positions. | ||
function _isSenderAuthorized(address user) internal view returns (bool) { | ||
return msg.sender == user || isAuthorized[user][msg.sender]; | ||
} | ||
|
||
// Interests management. | ||
/* INTEREST MANAGEMENT */ | ||
|
||
/// @dev Accrues interests for a market. | ||
/// @param market The market to accrue interests for. | ||
/// @param id The market's id. | ||
function _accrueInterests(Market memory market, Id id) internal { | ||
uint256 elapsed = block.timestamp - lastUpdate[id]; | ||
|
||
|
@@ -393,8 +418,12 @@ contract Blue is IBlue { | |
lastUpdate[id] = block.timestamp; | ||
} | ||
|
||
// Health check. | ||
/* HEALTH CHECK */ | ||
|
||
/// @notice Returns whether a position is healthy. | ||
/// @param market The market on which to check the health of `user`. | ||
/// @param id The market's id. | ||
/// @param user The user to check the health of. | ||
function _isHealthy(Market memory market, Id id, address user) internal view returns (bool) { | ||
if (borrowShares[id][user] == 0) return true; | ||
|
||
|
@@ -404,6 +433,12 @@ contract Blue is IBlue { | |
return _isHealthy(market, id, user, collateralPrice, borrowablePrice); | ||
} | ||
|
||
/// @notice Returns whether a position is healthy. | ||
/// @param market The market on which to check the health of `user`. | ||
/// @param id The market's id. | ||
/// @param user The user to check the health of. | ||
/// @param collateralPrice The collateral asset price. | ||
/// @param borrowablePrice The borrowable asset price. | ||
function _isHealthy(Market memory market, Id id, address user, uint256 collateralPrice, uint256 borrowablePrice) | ||
internal | ||
view | ||
|
@@ -416,8 +451,9 @@ contract Blue is IBlue { | |
return collateralValue.mulWadDown(market.lltv) >= borrowValue; | ||
} | ||
|
||
// Storage view. | ||
/* STORAGE VIEW */ | ||
|
||
/// @inheritdoc IBlue | ||
function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory res) { | ||
uint256 nSlots = slots.length; | ||
|
||
|
Oops, something went wrong.