forked from Benqi-fi/BENQI-Smart-Contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
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
Mikko Peltonen
committed
Aug 23, 2021
0 parents
commit fc511c5
Showing
29 changed files
with
6,596 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,85 @@ | ||
pragma solidity 0.5.17; | ||
|
||
/** | ||
* @title Careful Math | ||
* @author Benqi | ||
* @notice Derived from OpenZeppelin's SafeMath library | ||
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol | ||
*/ | ||
contract CarefulMath { | ||
|
||
/** | ||
* @dev Possible error codes that we can return | ||
*/ | ||
enum MathError { | ||
NO_ERROR, | ||
DIVISION_BY_ZERO, | ||
INTEGER_OVERFLOW, | ||
INTEGER_UNDERFLOW | ||
} | ||
|
||
/** | ||
* @dev Multiplies two numbers, returns an error on overflow. | ||
*/ | ||
function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { | ||
if (a == 0) { | ||
return (MathError.NO_ERROR, 0); | ||
} | ||
|
||
uint c = a * b; | ||
|
||
if (c / a != b) { | ||
return (MathError.INTEGER_OVERFLOW, 0); | ||
} else { | ||
return (MathError.NO_ERROR, c); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Integer division of two numbers, truncating the quotient. | ||
*/ | ||
function divUInt(uint a, uint b) internal pure returns (MathError, uint) { | ||
if (b == 0) { | ||
return (MathError.DIVISION_BY_ZERO, 0); | ||
} | ||
|
||
return (MathError.NO_ERROR, a / b); | ||
} | ||
|
||
/** | ||
* @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). | ||
*/ | ||
function subUInt(uint a, uint b) internal pure returns (MathError, uint) { | ||
if (b <= a) { | ||
return (MathError.NO_ERROR, a - b); | ||
} else { | ||
return (MathError.INTEGER_UNDERFLOW, 0); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Adds two numbers, returns an error on overflow. | ||
*/ | ||
function addUInt(uint a, uint b) internal pure returns (MathError, uint) { | ||
uint c = a + b; | ||
|
||
if (c >= a) { | ||
return (MathError.NO_ERROR, c); | ||
} else { | ||
return (MathError.INTEGER_OVERFLOW, 0); | ||
} | ||
} | ||
|
||
/** | ||
* @dev add a and b and then subtract c | ||
*/ | ||
function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { | ||
(MathError err0, uint sum) = addUInt(a, b); | ||
|
||
if (err0 != MathError.NO_ERROR) { | ||
return (err0, 0); | ||
} | ||
|
||
return subUInt(sum, c); | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,52 @@ | ||
pragma solidity 0.5.17; | ||
|
||
/** | ||
* @title The V2 & V3 Aggregator Interface | ||
* @notice Solidity V0.5 does not allow interfaces to inherit from other | ||
* interfaces so this contract is a combination of v0.5 AggregatorInterface.sol | ||
* and v0.5 AggregatorV3Interface.sol. | ||
*/ | ||
interface AggregatorV2V3Interface { | ||
// | ||
// V2 Interface: | ||
// | ||
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 timestamp); | ||
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); | ||
|
||
// | ||
// V3 Interface: | ||
// | ||
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 | ||
); | ||
} |
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 |
---|---|---|
@@ -0,0 +1,101 @@ | ||
pragma solidity 0.5.17; | ||
|
||
import "../PriceOracle.sol"; | ||
import "../QiErc20.sol"; | ||
import "../EIP20Interface.sol"; | ||
import "../SafeMath.sol"; | ||
import "./AggregatorV2V3Interface.sol"; | ||
|
||
contract BenqiChainlinkOracle is PriceOracle { | ||
using SafeMath for uint; | ||
address public admin; | ||
|
||
mapping(address => uint) internal prices; | ||
mapping(bytes32 => AggregatorV2V3Interface) internal feeds; | ||
event PricePosted(address asset, uint previousPriceMantissa, uint requestedPriceMantissa, uint newPriceMantissa); | ||
event NewAdmin(address oldAdmin, address newAdmin); | ||
event FeedSet(address feed, string symbol); | ||
|
||
constructor() public { | ||
admin = msg.sender; | ||
} | ||
|
||
function getUnderlyingPrice(QiToken qiToken) public view returns (uint) { | ||
string memory symbol = qiToken.symbol(); | ||
if (compareStrings(symbol, "qiAVAX")) { | ||
return getChainlinkPrice(getFeed(symbol)); | ||
} else { | ||
return getPrice(qiToken); | ||
} | ||
} | ||
|
||
function getPrice(QiToken qiToken) internal view returns (uint price) { | ||
EIP20Interface token = EIP20Interface(QiErc20(address(qiToken)).underlying()); | ||
|
||
if (prices[address(token)] != 0) { | ||
price = prices[address(token)]; | ||
} else { | ||
price = getChainlinkPrice(getFeed(token.symbol())); | ||
} | ||
|
||
uint decimalDelta = uint(18).sub(uint(token.decimals())); | ||
// Ensure that we don't multiply the result by 0 | ||
if (decimalDelta > 0) { | ||
return price.mul(10**decimalDelta); | ||
} else { | ||
return price; | ||
} | ||
} | ||
|
||
function getChainlinkPrice(AggregatorV2V3Interface feed) internal view returns (uint) { | ||
// Chainlink USD-denominated feeds store answers at 8 decimals | ||
uint decimalDelta = uint(18).sub(feed.decimals()); | ||
// Ensure that we don't multiply the result by 0 | ||
if (decimalDelta > 0) { | ||
return uint(feed.latestAnswer()).mul(10**decimalDelta); | ||
} else { | ||
return uint(feed.latestAnswer()); | ||
} | ||
} | ||
|
||
function setUnderlyingPrice(QiToken qiToken, uint underlyingPriceMantissa) external onlyAdmin() { | ||
address asset = address(QiErc20(address(qiToken)).underlying()); | ||
emit PricePosted(asset, prices[asset], underlyingPriceMantissa, underlyingPriceMantissa); | ||
prices[asset] = underlyingPriceMantissa; | ||
} | ||
|
||
function setDirectPrice(address asset, uint price) external onlyAdmin() { | ||
emit PricePosted(asset, prices[asset], price, price); | ||
prices[asset] = price; | ||
} | ||
|
||
function setFeed(string calldata symbol, address feed) external onlyAdmin() { | ||
require(feed != address(0) && feed != address(this), "invalid feed address"); | ||
emit FeedSet(feed, symbol); | ||
feeds[keccak256(abi.encodePacked(symbol))] = AggregatorV2V3Interface(feed); | ||
} | ||
|
||
function getFeed(string memory symbol) public view returns (AggregatorV2V3Interface) { | ||
return feeds[keccak256(abi.encodePacked(symbol))]; | ||
} | ||
|
||
function assetPrices(address asset) external view returns (uint) { | ||
return prices[asset]; | ||
} | ||
|
||
function compareStrings(string memory a, string memory b) internal pure returns (bool) { | ||
return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); | ||
} | ||
|
||
function setAdmin(address newAdmin) external onlyAdmin() { | ||
address oldAdmin = admin; | ||
admin = newAdmin; | ||
|
||
emit NewAdmin(oldAdmin, newAdmin); | ||
} | ||
|
||
modifier onlyAdmin() { | ||
require(msg.sender == admin, "only admin may call"); | ||
_; | ||
} | ||
} |
Oops, something went wrong.