/**************************************************************************
* ____ _
* / ___| | | __ _ _ _ ___ _ __
* | | _____ | | / _` || | | | / _ \| '__|
* | |___|_____|| |___| (_| || |_| || __/| |
* \____| |_____|\__,_| \__, | \___||_|
* |___/
*
**************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2016-2019 Cyril Lapinte
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************
*
* Flatten Contract: RatesProvider
*
* Git Commit:
* https://github.com/c-layer/contracts/tree/43925ba24cc22f42d0ff7711d0e169e8c2a0e09f
*
**************************************************************************/
// File: contracts/interface/IRatesProvider.sol
pragma solidity >=0.5.0 <0.6.0;
/**
* @title IRatesProvider
* @dev IRatesProvider interface
*
* @author Cyril Lapinte - <cyril.lapinte@openfiz.com>
*/
contract IRatesProvider {
function defineRatesExternal(uint256[] calldata _rates) external returns (bool);
function name() public view returns (string memory);
function rate(bytes32 _currency) public view returns (uint256);
function currencies() public view
returns (bytes32[] memory, uint256[] memory, uint256);
function rates() public view returns (uint256, uint256[] memory);
function convert(uint256 _amount, bytes32 _fromCurrency, bytes32 _toCurrency)
public view returns (uint256);
function defineCurrencies(
bytes32[] memory _currencies,
uint256[] memory _decimals,
uint256 _rateOffset) public returns (bool);
function defineRates(uint256[] memory _rates) public returns (bool);
event RateOffset(uint256 rateOffset);
event Currencies(bytes32[] currencies, uint256[] decimals);
event Rate(bytes32 indexed currency, uint256 rate);
}
// File: contracts/util/math/SafeMath.sol
pragma solidity >=0.5.0 <0.6.0;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/util/governance/Ownable.sol
pragma solidity >=0.5.0 <0.6.0;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
// File: contracts/util/governance/Operable.sol
pragma solidity >=0.5.0 <0.6.0;
/**
* @title Operable
* @dev The Operable contract enable the restrictions of operations to a set of operators
*
* @author Cyril Lapinte - <cyril.lapinte@openfiz.com>
*
* Error messages
* OP01: Message sender must be an operator
* OP02: Address must be an operator
* OP03: Address must not be an operator
*/
contract Operable is Ownable {
mapping (address => bool) private operators_;
/**
* @dev Throws if called by any account other than the operator
*/
modifier onlyOperator {
require(operators_[msg.sender], "OP01");
_;
}
/**
* @dev constructor
*/
constructor() public {
defineOperator("Owner", msg.sender);
}
/**
* @dev isOperator
* @param _address operator address
*/
function isOperator(address _address) public view returns (bool) {
return operators_[_address];
}
/**
* @dev removeOperator
* @param _address operator address
*/
function removeOperator(address _address) public onlyOwner {
require(operators_[_address], "OP02");
operators_[_address] = false;
emit OperatorRemoved(_address);
}
/**
* @dev defineOperator
* @param _role operator role
* @param _address operator address
*/
function defineOperator(string memory _role, address _address)
public onlyOwner
{
require(!operators_[_address], "OP03");
operators_[_address] = true;
emit OperatorDefined(_role, _address);
}
event OperatorRemoved(address address_);
event OperatorDefined(
string role,
address address_
);
}
// File: contracts/RatesProvider.sol
pragma solidity >=0.5.0 <0.6.0;
/**
* @title RatesProvider
* @dev RatesProvider interface
* @dev The null value for a rate indicates that the rate is undefined
* @dev It is recommended that smart contracts always check against a null rate
*
* @author Cyril Lapinte - <cyril.lapinte@openfiz.com>
*
* Error messages
* RP01: Currencies must match decimals length
* RP02: rateOffset cannot be null
* RP03: Rates definition must only target configured currencies
*/
contract RatesProvider is IRatesProvider, Operable {
using SafeMath for uint256;
string internal name_;
// decimals offset with which rates are stored using the counter currency
// this must be high enought to cover worse case
// Can only be set to 1 with ETH or ERC20 which already have 18 decimals
// It should likely be configured to 10**16 (18-2) for GBP, USD or CHF.
uint256 internal rateOffset_ = 1;
// The first currency will be the counter currency
bytes32[] internal currencies_ =
[ bytes32("ETH"), "BTC", "EOS", "GBP", "USD", "CHF", "EUR", "CNY", "JPY", "CAD", "AUD" ];
uint256[] internal decimals_ = [ uint256(18), 8, 4, 2, 2, 2, 2, 2, 2, 2, 2 ];
mapping(bytes32 => uint256) internal ratesMap;
uint256[] internal rates_ = new uint256[](currencies_.length-1);
uint256 internal updatedAt_;
/*
* @dev constructor
*/
constructor(string memory _name) public {
name_ = _name;
for (uint256 i=0; i < currencies_.length; i++) {
ratesMap[currencies_[i]] = i;
}
}
/**
* @dev define all rates
* @dev The rates should be defined in the base currency (WEI, Satoshi, cents, ...)
* @dev Rates should also account for rateOffset
*/
function defineRatesExternal(uint256[] calldata _rates)
external onlyOperator returns (bool)
{
require(_rates.length < currencies_.length, "RP03");
// solhint-disable-next-line not-rely-on-time
updatedAt_ = now;
for (uint256 i=0; i < _rates.length; i++) {
if (rates_[i] != _rates[i]) {
rates_[i] = _rates[i];
emit Rate(currencies_[i+1], _rates[i]);
}
}
return true;
}
/**
* @dev name
*/
function name() public view returns (string memory) {
return name_;
}
/**
* @dev rate for a currency
*/
function rate(bytes32 _currency) public view returns (uint256) {
return ratePrivate(_currency);
}
/**
* @dev currencies
*/
function currencies() public view
returns (bytes32[] memory, uint256[] memory, uint256)
{
return (currencies_, decimals_, rateOffset_);
}
/**
* @dev rate as store for a currency
*/
function rates() public view returns (uint256, uint256[] memory) {
return (updatedAt_, rates_);
}
/**
* @dev convert between two currency (base units)
*/
function convert(uint256 _amount, bytes32 _fromCurrency, bytes32 _toCurrency)
public view returns (uint256)
{
if (_fromCurrency == _toCurrency) {
return _amount;
}
uint256 rateFrom = (_fromCurrency != currencies_[0]) ?
ratePrivate(_fromCurrency) : rateOffset_;
uint256 rateTo = (_toCurrency != currencies_[0]) ?
ratePrivate(_toCurrency) : rateOffset_;
return (rateTo != 0) ?
_amount.mul(rateFrom).div(rateTo) : 0;
}
/**
* @dev define all currencies
* @dev @param _rateOffset is to be used when the default currency
* @dev does not have enough decimals for sufficient rate precisions
*/
function defineCurrencies(
bytes32[] memory _currencies,
uint256[] memory _decimals,
uint256 _rateOffset) public onlyOperator returns (bool)
{
require(_currencies.length == _decimals.length, "RP01");
require(_rateOffset != 0, "RP02");
for (uint256 i= _currencies.length; i < currencies_.length; i++) {
delete ratesMap[currencies_[i]];
emit Rate(currencies_[i], 0);
}
rates_.length = _currencies.length-1;
bool hasBaseCurrencyChanged = _currencies[0] != currencies_[0];
for (uint256 i=1; i < _currencies.length; i++) {
bytes32 currency = _currencies[i];
if (rateOffset_ != _rateOffset
|| ratesMap[currency] != i
|| hasBaseCurrencyChanged)
{
ratesMap[currency] = i;
rates_[i-1] = 0;
if (i < currencies_.length) {
emit Rate(currencies_[i], 0);
}
}
}
if (rateOffset_ != _rateOffset) {
emit RateOffset(_rateOffset);
rateOffset_ = _rateOffset;
}
// solhint-disable-next-line not-rely-on-time
updatedAt_ = now;
currencies_ = _currencies;
decimals_ = _decimals;
emit Currencies(_currencies, _decimals);
return true;
}
/**
* @dev define all rates
* @dev The rates should be defined in the base currency (WEI, Satoshi, cents, ...)
* @dev Rates should also account for rateOffset
*/
function defineRates(uint256[] memory _rates)
public onlyOperator returns (bool)
{
require(_rates.length < currencies_.length, "RP03");
// solhint-disable-next-line not-rely-on-time
updatedAt_ = now;
for (uint256 i=0; i < _rates.length; i++) {
if (rates_[i] != _rates[i]) {
rates_[i] = _rates[i];
emit Rate(currencies_[i+1], _rates[i]);
}
}
return true;
}
/**
* @dev rate for a currency
*/
function ratePrivate(bytes32 _currency) private view returns (uint256) {
if (_currency == currencies_[0]) {
return 1;
}
uint256 id = ratesMap[_currency];
return (id > 0) ? rates_[id-1] : 0;
}
}
{
"compilationTarget": {
"RatesProvider.sol": "RatesProvider"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32[]","name":"currencies","type":"bytes32[]"},{"indexed":false,"internalType":"uint256[]","name":"decimals","type":"uint256[]"}],"name":"Currencies","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"role","type":"string"},{"indexed":false,"internalType":"address","name":"address_","type":"address"}],"name":"OperatorDefined","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"address_","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"currency","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"Rate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rateOffset","type":"uint256"}],"name":"RateOffset","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes32","name":"_fromCurrency","type":"bytes32"},{"internalType":"bytes32","name":"_toCurrency","type":"bytes32"}],"name":"convert","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currencies","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32[]","name":"_currencies","type":"bytes32[]"},{"internalType":"uint256[]","name":"_decimals","type":"uint256[]"},{"internalType":"uint256","name":"_rateOffset","type":"uint256"}],"name":"defineCurrencies","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"_role","type":"string"},{"internalType":"address","name":"_address","type":"address"}],"name":"defineOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"_rates","type":"uint256[]"}],"name":"defineRates","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"_rates","type":"uint256[]"}],"name":"defineRatesExternal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"_currency","type":"bytes32"}],"name":"rate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rates","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"removeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]