Skip to content

Commit

Permalink
Publish BENQI smart contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikko Peltonen committed Aug 23, 2021
0 parents commit fc511c5
Show file tree
Hide file tree
Showing 29 changed files with 6,596 additions and 0 deletions.
85 changes: 85 additions & 0 deletions CarefulMath.sol
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);
}
}
52 changes: 52 additions & 0 deletions Chainlink/AggregatorV2V3Interface.sol
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
);
}
101 changes: 101 additions & 0 deletions Chainlink/BenqiChainlinkOracle.sol
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");
_;
}
}
Loading

0 comments on commit fc511c5

Please sign in to comment.