Created
December 22, 2020 15:22
-
-
Save alexroan/fa1660cf297d3f5fce6efb8c071cb9ee to your computer and use it in GitHub Desktop.
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
pragma solidity ^0.5.0; | |
interface AggregatorV3Interface { | |
function decimals() external view returns (uint8); | |
function description() external view returns (string memory); | |
function version() external view returns (uint256); | |
// getRoundData and latestRoundData should both raise "No data present" | |
// if they do not have data to report, instead of returning unset values | |
// which could be misinterpreted as actual reported values. | |
function getRoundData(uint80 _roundId) | |
external | |
view | |
returns ( | |
uint80 roundId, | |
int256 answer, | |
uint256 startedAt, | |
uint256 updatedAt, | |
uint80 answeredInRound | |
); | |
function latestRoundData() | |
external | |
view | |
returns ( | |
uint80 roundId, | |
int256 answer, | |
uint256 startedAt, | |
uint256 updatedAt, | |
uint80 answeredInRound | |
); | |
} | |
interface AggregatorInterface { | |
function latestAnswer() external view returns (int256); | |
function latestTimestamp() external view returns (uint256); | |
function latestRound() external view returns (uint256); | |
function getAnswer(uint256 roundId) external view returns (int256); | |
function getTimestamp(uint256 roundId) external view returns (uint256); | |
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); | |
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); | |
} | |
contract ComptrollerInterface { | |
/// @notice Indicator that this is a Comptroller contract (for inspection) | |
bool public constant isComptroller = true; | |
/*** Assets You Are In ***/ | |
function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); | |
function exitMarket(address cToken) external returns (uint); | |
/*** Policy Hooks ***/ | |
function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); | |
function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; | |
function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); | |
function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; | |
function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); | |
function borrowVerify(address cToken, address borrower, uint borrowAmount) external; | |
function repayBorrowAllowed( | |
address cToken, | |
address payer, | |
address borrower, | |
uint repayAmount) external returns (uint); | |
function repayBorrowVerify( | |
address cToken, | |
address payer, | |
address borrower, | |
uint repayAmount, | |
uint borrowerIndex) external; | |
function liquidateBorrowAllowed( | |
address cTokenBorrowed, | |
address cTokenCollateral, | |
address liquidator, | |
address borrower, | |
uint repayAmount) external returns (uint); | |
function liquidateBorrowVerify( | |
address cTokenBorrowed, | |
address cTokenCollateral, | |
address liquidator, | |
address borrower, | |
uint repayAmount, | |
uint seizeTokens) external; | |
function seizeAllowed( | |
address cTokenCollateral, | |
address cTokenBorrowed, | |
address liquidator, | |
address borrower, | |
uint seizeTokens) external returns (uint); | |
function seizeVerify( | |
address cTokenCollateral, | |
address cTokenBorrowed, | |
address liquidator, | |
address borrower, | |
uint seizeTokens) external; | |
function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); | |
function transferVerify(address cToken, address src, address dst, uint transferTokens) external; | |
/*** Liquidity/Liquidation Calculations ***/ | |
function liquidateCalculateSeizeTokens( | |
address cTokenBorrowed, | |
address cTokenCollateral, | |
uint repayAmount) external view returns (uint, uint); | |
} | |
contract InterestRateModel { | |
/// @notice Indicator that this is an InterestRateModel contract (for inspection) | |
bool public constant isInterestRateModel = true; | |
/** | |
* @notice Calculates the current borrow interest rate per block | |
* @param cash The total amount of cash the market has | |
* @param borrows The total amount of borrows the market has outstanding | |
* @param reserves The total amount of reserves the market has | |
* @return The borrow rate per block (as a percentage, and scaled by 1e18) | |
*/ | |
function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); | |
/** | |
* @notice Calculates the current supply interest rate per block | |
* @param cash The total amount of cash the market has | |
* @param borrows The total amount of borrows the market has outstanding | |
* @param reserves The total amount of reserves the market has | |
* @param reserveFactorMantissa The current reserve factor the market has | |
* @return The supply rate per block (as a percentage, and scaled by 1e18) | |
*/ | |
function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); | |
} | |
contract CTokenStorage { | |
/** | |
* @dev Guard variable for re-entrancy checks | |
*/ | |
bool internal _notEntered; | |
/** | |
* @notice EIP-20 token name for this token | |
*/ | |
string public name; | |
/** | |
* @notice EIP-20 token symbol for this token | |
*/ | |
string public symbol; | |
/** | |
* @notice EIP-20 token decimals for this token | |
*/ | |
uint8 public decimals; | |
uint internal constant borrowRateMaxMantissa = 0.0005e16; | |
uint internal constant reserveFactorMaxMantissa = 1e18; | |
/** | |
* @notice Administrator for this contract | |
*/ | |
address payable public admin; | |
/** | |
* @notice Pending administrator for this contract | |
*/ | |
address payable public pendingAdmin; | |
ComptrollerInterface public comptroller; | |
InterestRateModel public interestRateModel; | |
uint internal initialExchangeRateMantissa; | |
/** | |
* @notice Fraction of interest currently set aside for reserves | |
*/ | |
uint public reserveFactorMantissa; | |
/** | |
* @notice Block number that interest was last accrued at | |
*/ | |
uint public accrualBlockNumber; | |
/** | |
* @notice Accumulator of the total earned interest rate since the opening of the market | |
*/ | |
uint public borrowIndex; | |
/** | |
* @notice Total amount of outstanding borrows of the underlying in this market | |
*/ | |
uint public totalBorrows; | |
/** | |
* @notice Total amount of reserves of the underlying held in this market | |
*/ | |
uint public totalReserves; | |
/** | |
* @notice Total number of tokens in circulation | |
*/ | |
uint public totalSupply; | |
mapping (address => uint) internal accountTokens; | |
mapping (address => mapping (address => uint)) internal transferAllowances; | |
/** | |
* @notice Container for borrow balance information | |
* @member principal Total balance (with accrued interest), after applying the most recent balance-changing action | |
* @member interestIndex Global borrowIndex as of the most recent balance-changing action | |
*/ | |
struct BorrowSnapshot { | |
uint principal; | |
uint interestIndex; | |
} | |
mapping(address => BorrowSnapshot) internal accountBorrows; | |
} | |
contract CTokenInterface is CTokenStorage { | |
/** | |
* @notice Indicator that this is a CToken contract (for inspection) | |
*/ | |
bool public constant isCToken = true; | |
/*** Market Events ***/ | |
/** | |
* @notice Event emitted when interest is accrued | |
*/ | |
event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); | |
/** | |
* @notice Event emitted when tokens are minted | |
*/ | |
event Mint(address minter, uint mintAmount, uint mintTokens); | |
/** | |
* @notice Event emitted when tokens are redeemed | |
*/ | |
event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); | |
/** | |
* @notice Event emitted when underlying is borrowed | |
*/ | |
event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); | |
/** | |
* @notice Event emitted when a borrow is repaid | |
*/ | |
event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); | |
/** | |
* @notice Event emitted when a borrow is liquidated | |
*/ | |
event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); | |
/*** Admin Events ***/ | |
/** | |
* @notice Event emitted when pendingAdmin is changed | |
*/ | |
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); | |
/** | |
* @notice Event emitted when pendingAdmin is accepted, which means admin is updated | |
*/ | |
event NewAdmin(address oldAdmin, address newAdmin); | |
/** | |
* @notice Event emitted when comptroller is changed | |
*/ | |
event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); | |
/** | |
* @notice Event emitted when interestRateModel is changed | |
*/ | |
event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); | |
/** | |
* @notice Event emitted when the reserve factor is changed | |
*/ | |
event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); | |
/** | |
* @notice Event emitted when the reserves are added | |
*/ | |
event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); | |
/** | |
* @notice Event emitted when the reserves are reduced | |
*/ | |
event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); | |
/** | |
* @notice EIP20 Transfer event | |
*/ | |
event Transfer(address indexed from, address indexed to, uint amount); | |
/** | |
* @notice EIP20 Approval event | |
*/ | |
event Approval(address indexed owner, address indexed spender, uint amount); | |
/** | |
* @notice Failure event | |
*/ | |
event Failure(uint error, uint info, uint detail); | |
/*** User Interface ***/ | |
function transfer(address dst, uint amount) external returns (bool); | |
function transferFrom(address src, address dst, uint amount) external returns (bool); | |
function approve(address spender, uint amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint); | |
function balanceOf(address owner) external view returns (uint); | |
function balanceOfUnderlying(address owner) external returns (uint); | |
function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); | |
function borrowRatePerBlock() external view returns (uint); | |
function supplyRatePerBlock() external view returns (uint); | |
function totalBorrowsCurrent() external returns (uint); | |
function borrowBalanceCurrent(address account) external returns (uint); | |
function borrowBalanceStored(address account) public view returns (uint); | |
function exchangeRateCurrent() public returns (uint); | |
function exchangeRateStored() public view returns (uint); | |
function getCash() external view returns (uint); | |
function accrueInterest() public returns (uint); | |
function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); | |
/*** Admin Functions ***/ | |
function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); | |
function _acceptAdmin() external returns (uint); | |
function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); | |
function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); | |
function _reduceReserves(uint reduceAmount) external returns (uint); | |
function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); | |
} | |
contract PriceOracle { | |
/// @notice Indicator that this is a PriceOracle contract (for inspection) | |
bool public constant isPriceOracle = true; | |
/** | |
* @notice Get the underlying price of a cToken asset | |
* @param cToken The cToken to get the underlying price of | |
* @return The underlying asset price mantissa (scaled by 1e18). | |
* Zero means the price is unavailable. | |
*/ | |
function getUnderlyingPrice(CTokenInterface cToken) external view returns (uint); | |
} | |
contract ComptrollerErrorReporter { | |
enum Error { | |
NO_ERROR, | |
UNAUTHORIZED, | |
COMPTROLLER_MISMATCH, | |
INSUFFICIENT_SHORTFALL, | |
INSUFFICIENT_LIQUIDITY, | |
INVALID_CLOSE_FACTOR, | |
INVALID_COLLATERAL_FACTOR, | |
INVALID_LIQUIDATION_INCENTIVE, | |
MARKET_NOT_ENTERED, // no longer possible | |
MARKET_NOT_LISTED, | |
MARKET_ALREADY_LISTED, | |
MATH_ERROR, | |
NONZERO_BORROW_BALANCE, | |
PRICE_ERROR, | |
REJECTION, | |
SNAPSHOT_ERROR, | |
TOO_MANY_ASSETS, | |
TOO_MUCH_REPAY | |
} | |
enum FailureInfo { | |
ACCEPT_ADMIN_PENDING_ADMIN_CHECK, | |
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, | |
EXIT_MARKET_BALANCE_OWED, | |
EXIT_MARKET_REJECTION, | |
SET_CLOSE_FACTOR_OWNER_CHECK, | |
SET_CLOSE_FACTOR_VALIDATION, | |
SET_COLLATERAL_FACTOR_OWNER_CHECK, | |
SET_COLLATERAL_FACTOR_NO_EXISTS, | |
SET_COLLATERAL_FACTOR_VALIDATION, | |
SET_COLLATERAL_FACTOR_WITHOUT_PRICE, | |
SET_IMPLEMENTATION_OWNER_CHECK, | |
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, | |
SET_LIQUIDATION_INCENTIVE_VALIDATION, | |
SET_MAX_ASSETS_OWNER_CHECK, | |
SET_PENDING_ADMIN_OWNER_CHECK, | |
SET_PENDING_IMPLEMENTATION_OWNER_CHECK, | |
SET_PRICE_ORACLE_OWNER_CHECK, | |
SUPPORT_MARKET_EXISTS, | |
SUPPORT_MARKET_OWNER_CHECK, | |
SET_PAUSE_GUARDIAN_OWNER_CHECK | |
} | |
/** | |
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary | |
* contract-specific code that enables us to report opaque error codes from upgradeable contracts. | |
**/ | |
event Failure(uint error, uint info, uint detail); | |
/** | |
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator | |
*/ | |
function fail(Error err, FailureInfo info) internal returns (uint) { | |
emit Failure(uint(err), uint(info), 0); | |
return uint(err); | |
} | |
/** | |
* @dev use this when reporting an opaque error from an upgradeable collaborator contract | |
*/ | |
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { | |
emit Failure(uint(err), uint(info), opaqueError); | |
return uint(err); | |
} | |
} | |
contract TokenErrorReporter { | |
enum Error { | |
NO_ERROR, | |
UNAUTHORIZED, | |
BAD_INPUT, | |
COMPTROLLER_REJECTION, | |
COMPTROLLER_CALCULATION_ERROR, | |
INTEREST_RATE_MODEL_ERROR, | |
INVALID_ACCOUNT_PAIR, | |
INVALID_CLOSE_AMOUNT_REQUESTED, | |
INVALID_COLLATERAL_FACTOR, | |
MATH_ERROR, | |
MARKET_NOT_FRESH, | |
MARKET_NOT_LISTED, | |
TOKEN_INSUFFICIENT_ALLOWANCE, | |
TOKEN_INSUFFICIENT_BALANCE, | |
TOKEN_INSUFFICIENT_CASH, | |
TOKEN_TRANSFER_IN_FAILED, | |
TOKEN_TRANSFER_OUT_FAILED | |
} | |
/* | |
* Note: FailureInfo (but not Error) is kept in alphabetical order | |
* This is because FailureInfo grows significantly faster, and | |
* the order of Error has some meaning, while the order of FailureInfo | |
* is entirely arbitrary. | |
*/ | |
enum FailureInfo { | |
ACCEPT_ADMIN_PENDING_ADMIN_CHECK, | |
ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, | |
ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED, | |
ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, | |
ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, | |
ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, | |
ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, | |
BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, | |
BORROW_ACCRUE_INTEREST_FAILED, | |
BORROW_CASH_NOT_AVAILABLE, | |
BORROW_FRESHNESS_CHECK, | |
BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, | |
BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, | |
BORROW_MARKET_NOT_LISTED, | |
BORROW_COMPTROLLER_REJECTION, | |
LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED, | |
LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED, | |
LIQUIDATE_COLLATERAL_FRESHNESS_CHECK, | |
LIQUIDATE_COMPTROLLER_REJECTION, | |
LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED, | |
LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX, | |
LIQUIDATE_CLOSE_AMOUNT_IS_ZERO, | |
LIQUIDATE_FRESHNESS_CHECK, | |
LIQUIDATE_LIQUIDATOR_IS_BORROWER, | |
LIQUIDATE_REPAY_BORROW_FRESH_FAILED, | |
LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, | |
LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, | |
LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, | |
LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER, | |
LIQUIDATE_SEIZE_TOO_MUCH, | |
MINT_ACCRUE_INTEREST_FAILED, | |
MINT_COMPTROLLER_REJECTION, | |
MINT_EXCHANGE_CALCULATION_FAILED, | |
MINT_EXCHANGE_RATE_READ_FAILED, | |
MINT_FRESHNESS_CHECK, | |
MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, | |
MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, | |
MINT_TRANSFER_IN_FAILED, | |
MINT_TRANSFER_IN_NOT_POSSIBLE, | |
REDEEM_ACCRUE_INTEREST_FAILED, | |
REDEEM_COMPTROLLER_REJECTION, | |
REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, | |
REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, | |
REDEEM_EXCHANGE_RATE_READ_FAILED, | |
REDEEM_FRESHNESS_CHECK, | |
REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, | |
REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, | |
REDEEM_TRANSFER_OUT_NOT_POSSIBLE, | |
REDUCE_RESERVES_ACCRUE_INTEREST_FAILED, | |
REDUCE_RESERVES_ADMIN_CHECK, | |
REDUCE_RESERVES_CASH_NOT_AVAILABLE, | |
REDUCE_RESERVES_FRESH_CHECK, | |
REDUCE_RESERVES_VALIDATION, | |
REPAY_BEHALF_ACCRUE_INTEREST_FAILED, | |
REPAY_BORROW_ACCRUE_INTEREST_FAILED, | |
REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, | |
REPAY_BORROW_COMPTROLLER_REJECTION, | |
REPAY_BORROW_FRESHNESS_CHECK, | |
REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, | |
REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, | |
REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE, | |
SET_COLLATERAL_FACTOR_OWNER_CHECK, | |
SET_COLLATERAL_FACTOR_VALIDATION, | |
SET_COMPTROLLER_OWNER_CHECK, | |
SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED, | |
SET_INTEREST_RATE_MODEL_FRESH_CHECK, | |
SET_INTEREST_RATE_MODEL_OWNER_CHECK, | |
SET_MAX_ASSETS_OWNER_CHECK, | |
SET_ORACLE_MARKET_NOT_LISTED, | |
SET_PENDING_ADMIN_OWNER_CHECK, | |
SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED, | |
SET_RESERVE_FACTOR_ADMIN_CHECK, | |
SET_RESERVE_FACTOR_FRESH_CHECK, | |
SET_RESERVE_FACTOR_BOUNDS_CHECK, | |
TRANSFER_COMPTROLLER_REJECTION, | |
TRANSFER_NOT_ALLOWED, | |
TRANSFER_NOT_ENOUGH, | |
TRANSFER_TOO_MUCH, | |
ADD_RESERVES_ACCRUE_INTEREST_FAILED, | |
ADD_RESERVES_FRESH_CHECK, | |
ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE | |
} | |
/** | |
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary | |
* contract-specific code that enables us to report opaque error codes from upgradeable contracts. | |
**/ | |
event Failure(uint error, uint info, uint detail); | |
/** | |
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator | |
*/ | |
function fail(Error err, FailureInfo info) internal returns (uint) { | |
emit Failure(uint(err), uint(info), 0); | |
return uint(err); | |
} | |
/** | |
* @dev use this when reporting an opaque error from an upgradeable collaborator contract | |
*/ | |
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { | |
emit Failure(uint(err), uint(info), opaqueError); | |
return uint(err); | |
} | |
} | |
contract OracleErrorReporter { | |
enum Error { | |
NO_ERROR, | |
UNAUTHORIZED | |
} | |
enum FailureInfo { | |
SET_PRICE_FEED_OWNER_CHECK | |
} | |
/** | |
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary | |
* contract-specific code that enables us to report opaque error codes from upgradeable contracts. | |
**/ | |
event Failure(uint error, uint info, uint detail); | |
/** | |
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator | |
*/ | |
function fail(Error err, FailureInfo info) internal returns (uint) { | |
emit Failure(uint(err), uint(info), 0); | |
return uint(err); | |
} | |
} | |
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol | |
// Subject to the MIT license. | |
/** | |
* @dev Wrappers over Solidity's arithmetic operations with added overflow | |
* checks. | |
* | |
* Arithmetic operations in Solidity wrap on overflow. This can easily result | |
* in bugs, because programmers usually assume that an overflow raises an | |
* error, which is the standard behavior in high level programming languages. | |
* `SafeMath` restores this intuition by reverting the transaction when an | |
* operation overflows. | |
* | |
* Using this library instead of the unchecked operations eliminates an entire | |
* class of bugs, so it's recommended to use it always. | |
*/ | |
library SafeMath { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, errorMessage); | |
return c; | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* - Subtraction cannot underflow. | |
*/ | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction underflow"); | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* - Subtraction cannot underflow. | |
*/ | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, errorMessage); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. | |
* Reverts on division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. | |
* Reverts with custom message on division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts with custom message when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
contract ChainlinkPriceOracle is PriceOracle, OracleErrorReporter { | |
using SafeMath for uint; | |
struct PriceFeed { | |
AggregatorV3Interface feed; | |
uint extraDecimals; | |
} | |
/** | |
* @notice Administrator for this contract | |
*/ | |
address public admin; | |
/** | |
* @notice Mapping of (cToken Address => PriceFeed) | |
*/ | |
mapping(address => PriceFeed) public priceFeeds; | |
/** | |
* @notice Emitted when an existing price feed is replaced for a cToken | |
*/ | |
event PriceFeedSet(address indexed cTokenAddress, address indexed oldPriceFeed, address indexed newPriceFeed, uint extraDecimals); | |
/** | |
* @notice Create a ChailninkPriceOracle contract | |
*/ | |
constructor() public { | |
admin = msg.sender; | |
} | |
/** | |
* @notice Get the underlying price of a cToken asset | |
* @param cToken The cToken to get the underlying price of | |
* @return The underlying asset price | |
*/ | |
function getUnderlyingPrice(CTokenInterface cToken) public view returns (uint) { | |
PriceFeed memory priceFeed = priceFeeds[address(cToken)]; | |
// Check that a price feed exists for the cToken | |
AggregatorV3Interface feed = priceFeed.feed; | |
require(address(feed) != address(0), "Price feed doesn't exist"); | |
// Get the price | |
(,int price,,,) = feed.latestRoundData(); | |
// Check that the price is not negative | |
require(price >= 0, "Price cannot be negative"); | |
// Return transformed price | |
return uint(price).mul(10**priceFeed.extraDecimals); | |
} | |
/*** Admin Functions ***/ | |
/** | |
* @notice Add a price feed for a cToken | |
* @param cTokenAddress The address of the cToken | |
* @param newPriceFeedAddress The address of the price feed | |
* @param newExtraDecimals The extra decimals for the price feed | |
* @return Whether or not the price feed was set | |
*/ | |
function _setPriceFeed(address cTokenAddress, address newPriceFeedAddress, uint newExtraDecimals) external returns (uint) { | |
// Check caller is admin | |
if (msg.sender != admin) { | |
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_FEED_OWNER_CHECK); | |
} | |
// Get the old feed | |
address oldPriceFeedAddress = address(priceFeeds[cTokenAddress].feed); | |
// Set new feed | |
priceFeeds[cTokenAddress] = PriceFeed( | |
AggregatorV3Interface(newPriceFeedAddress), | |
newExtraDecimals | |
); | |
// Emit that a price feed has been added | |
emit PriceFeedSet(cTokenAddress, oldPriceFeedAddress, newPriceFeedAddress, newExtraDecimals); | |
return uint(Error.NO_ERROR); | |
} | |
} | |
contract GasMeasure { | |
CTokenInterface cToken; | |
ChainlinkPriceOracle oracle; | |
constructor() public{ | |
cToken = CTokenInterface(0x52929847A44B243E7212a1ea8b505A436757d761); | |
oracle = new ChainlinkPriceOracle(); | |
oracle._setPriceFeed(address(cToken), 0x8e67A0CFfbbF6A346ce87DFe06daE2dc782b3219, 10); | |
} | |
function measureGasCost() public view returns (uint, uint) { | |
ChainlinkPriceOracle myOracle = oracle; | |
uint startingGas = gasleft(); | |
uint price = myOracle.getUnderlyingPrice(cToken); | |
uint finishingGas = gasleft(); | |
return (startingGas - finishingGas, price); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment