Skip to content

Stable oracle #546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions contracts/SecurityTokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
bytes32 constant POLYMATHREGISTRY = 0x90eeab7c36075577c7cc5ff366e389fefa8a18289b949bab3529ab4471139d4d;
bytes32 constant STRGETTER = 0x982f24b3bd80807ec3cb227ba152e15c07d66855fa8ae6ca536e689205c0e2e9;

string constant POLY_ORACLE = "PolyUsdOracle";
string constant POLY_ORACLE = "StablePolyUsdOracle";

// Emit when network becomes paused
event Pause(uint256 _timestammp);
Expand Down Expand Up @@ -214,13 +214,22 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
/**
* @notice Converts USD fees into POLY amounts
*/
function _takeFee(bytes32 _feeType) internal returns (uint256, uint256){
function _takeFee(bytes32 _feeType) internal returns (uint256, uint256) {
(uint256 usdFee, uint256 polyFee) = getFees(_feeType);
if (polyFee > 0)
require(IERC20(getAddressValue(POLYTOKEN)).transferFrom(msg.sender, address(this), polyFee), "Insufficent allowance");
return (usdFee, polyFee);
}

/**
* @notice Returns the usd & poly fee for a particular feetype
* @param _feeType Key corresponding to fee type
*/
function getFees(bytes32 _feeType) public returns (uint256, uint256) {
address polymathRegistry = getAddressValue(POLYMATHREGISTRY);
uint256 polyRate = IOracle(IPolymathRegistry(polymathRegistry).getAddress(POLY_ORACLE)).getPrice();
uint256 usdFee = getUintValue(_feeType);
uint256 polyFee = DecimalMath.div(usdFee, polyRate);
if (polyFee > 0)
require(IERC20(getAddressValue(POLYTOKEN)).transferFrom(msg.sender, address(this), polyFee), "Insufficent allowance");
return (usdFee, polyFee);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IModuleFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface IModuleFactory {
/**
* @notice Get the setup cost of the module
*/
function getSetupCostInPoly() external view returns (uint256);
function getSetupCostInPoly() external returns (uint256);

/**
* @notice Used to get the lower bound
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ interface IOracle {
/**
* @notice Returns price - should throw if not valid
*/
function getPrice() external view returns(uint256);
function getPrice() external returns(uint256);

}
29 changes: 2 additions & 27 deletions contracts/libraries/TokenLib.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pragma solidity ^0.5.0;

import "../modules/PermissionManager/IPermissionManager.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../interfaces/IPoly.sol";

Expand Down Expand Up @@ -157,30 +156,6 @@ library TokenLib {
emit ModuleBudgetChanged(_modulesToData[_module].moduleTypes, _module, currentAllowance, newAllowance);
}

/**
* @notice Validates permissions with PermissionManager if it exists. If there's no permission return false
* @dev Note that IModule withPerm will allow ST owner all permissions by default
* @dev this allows individual modules to override this logic if needed (to not allow ST owner all permissions)
* @param _modules is the modules to check permissions on
* @param _delegate is the address of the delegate
* @param _module is the address of the PermissionManager module
* @param _perm is the permissions data
* @return success
*/
function checkPermission(address[] storage _modules, address _delegate, address _module, bytes32 _perm) public view returns(bool) {
if (_modules.length == 0) {
return false;
}

for (uint8 i = 0; i < _modules.length; i++) {
if (IPermissionManager(_modules[i]).checkPermission(_delegate, _module, _perm)) {
return true;
}
}

return false;
}

/**
* @notice Queries a value at a defined checkpoint
* @param _checkpoints is array of Checkpoint objects
Expand Down Expand Up @@ -256,8 +231,8 @@ library TokenLib {
uint256 _value,
uint256 _balanceTo,
uint256 _balanceFrom
)
public
)
public
{
if ((_value == 0) || (_from == _to)) {
return;
Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/MockOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract MockOracle is IOracle {
/**
* @notice Returns price - should throw if not valid
*/
function getPrice() external view returns(uint256) {
function getPrice() external returns(uint256) {
return price;
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/modules/ModuleFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract ModuleFactory is IModuleFactory, Ownable {
bytes32 public name;
string public title;

string constant POLY_ORACLE = "PolyUsdOracle";
string constant POLY_ORACLE = "StablePolyUsdOracle";

// @notice Allow only two variables to be stored
// 1. lowerBound
Expand Down Expand Up @@ -142,7 +142,7 @@ contract ModuleFactory is IModuleFactory, Ownable {
/**
* @notice Get the setup cost of the module
*/
function getSetupCostInPoly() public view returns (uint256) {
function getSetupCostInPoly() public returns (uint256) {
uint256 polyRate = IOracle(IPolymathRegistry(polymathRegistry).getAddress(POLY_ORACLE)).getPrice();
return DecimalMath.div(setupCost, polyRate);
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/modules/STO/USDTieredSTO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO {
* @dev returns current conversion rate of funds
* @param _fundRaiseType Fund raise type to get rate of
*/
function getRate(FundRaiseType _fundRaiseType) public view returns (uint256) {
function getRate(FundRaiseType _fundRaiseType) public returns (uint256) {
if (_fundRaiseType == FundRaiseType.ETH) {
return IOracle(_getOracle(bytes32("ETH"), bytes32("USD"))).getPrice();
} else if (_fundRaiseType == FundRaiseType.POLY) {
Expand All @@ -614,7 +614,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO {
* @param _amount Value to convert to USD
* @return uint256 Value in USD
*/
function convertToUSD(FundRaiseType _fundRaiseType, uint256 _amount) public view returns(uint256) {
function convertToUSD(FundRaiseType _fundRaiseType, uint256 _amount) public returns(uint256) {
return DecimalMath.mul(_amount, getRate(_fundRaiseType));
}

Expand All @@ -624,7 +624,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO {
* @param _amount Value to convert from USD
* @return uint256 Value in ETH or POLY
*/
function convertFromUSD(FundRaiseType _fundRaiseType, uint256 _amount) public view returns(uint256) {
function convertFromUSD(FundRaiseType _fundRaiseType, uint256 _amount) public returns(uint256) {
return DecimalMath.div(_amount, getRate(_fundRaiseType));
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/oracles/MakerDAOOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract MakerDAOOracle is IOracle, Ownable {
/**
* @notice Returns price - should throw if not valid
*/
function getPrice() external view returns(uint256) {
function getPrice() external returns(uint256) {
if (manualOverride) {
return manualPrice;
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/oracles/PolyOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ contract PolyOracle is usingOraclize, IOracle, Ownable {
/**
* @notice Returns price - should throw if not valid
*/
function getPrice() external view returns(uint256) {
function getPrice() external returns(uint256) {
/*solium-disable-next-line security/no-block-members*/
require(latestUpdate >= now - staleTime, "Invalid price");
return POLYUSD;
Expand Down
113 changes: 113 additions & 0 deletions contracts/oracles/StableOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
pragma solidity ^0.5.0;

import "../interfaces/IOracle.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

contract StableOracle is IOracle, Ownable {
using SafeMath for uint256;

IOracle public oracle;
uint256 public lastPrice;
uint256 public evictPercentage; //% multiplid by 10**16

bool public manualOverride;
uint256 public manualPrice;

/*solium-disable-next-line security/no-block-members*/
event ChangeOracle(address _oldOracle, address _newOracle);
event ChangeEvictPercentage(uint256 _oldEvictPercentage, uint256 _newEvictPercentage);
event SetManualPrice(uint256 _oldPrice, uint256 _newPrice, uint256 _time);
event SetManualOverride(bool _override, uint256 _time);

/**
* @notice Creates a new stable oracle based on existing oracle
* @param _oracle address of underlying oracle
*/
constructor(address _oracle, uint256 _evictPercentage) public {
require(_oracle != address(0), "Invalid oracle");
oracle = IOracle(_oracle);
evictPercentage = _evictPercentage;
}

/**
* @notice Updates medianizer address
* @param _oracle Address of underlying oracle
*/
function changeOracle(address _oracle) public onlyOwner {
require(_oracle != address(0), "Invalid oracle");
/*solium-disable-next-line security/no-block-members*/
emit ChangeOracle(address(oracle), _oracle);
oracle = IOracle(_oracle);
}

/**
* @notice Updates eviction percentage
* @param _evictPercentage Percentage multiplied by 10**16
*/
function changeEvictPercentage(uint256 _evictPercentage) public onlyOwner {
emit ChangeEvictPercentage(evictPercentage, _evictPercentage);
evictPercentage = _evictPercentage;
}

/**
* @notice Returns address of oracle currency (0x0 for ETH)
*/
function getCurrencyAddress() external view returns(address) {
return oracle.getCurrencyAddress();
}

/**
* @notice Returns symbol of oracle currency (0x0 for ETH)
*/
function getCurrencySymbol() external view returns(bytes32) {
return oracle.getCurrencySymbol();
}

/**
* @notice Returns denomination of price
*/
function getCurrencyDenominated() external view returns(bytes32) {
return oracle.getCurrencyDenominated();
}

/**
* @notice Returns price - should throw if not valid
*/
function getPrice() external returns(uint256) {
if (manualOverride) {
return manualPrice;
}
uint256 currentPrice = oracle.getPrice();
if ((lastPrice == 0) || (_change(currentPrice, lastPrice) >= evictPercentage)) {
lastPrice = currentPrice;
}
return lastPrice;
}

function _change(uint256 _newPrice, uint256 _oldPrice) internal pure returns(uint256) {
uint256 diff = _newPrice > _oldPrice ? _newPrice.sub(_oldPrice) : _oldPrice.sub(_newPrice);
return diff.mul(10**18).div(_oldPrice);
}

/**
* @notice Set a manual price. NA - this will only be used if manualOverride == true
* @param _price Price to set
*/
function setManualPrice(uint256 _price) public onlyOwner {
/*solium-disable-next-line security/no-block-members*/
emit SetManualPrice(manualPrice, _price, now);
manualPrice = _price;
}

/**
* @notice Determine whether manual price is used or not
* @param _override Whether to use the manual override price or not
*/
function setManualOverride(bool _override) public onlyOwner {
manualOverride = _override;
/*solium-disable-next-line security/no-block-members*/
emit SetManualOverride(_override, now);
}

}
42 changes: 21 additions & 21 deletions contracts/tokens/SecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "../interfaces/IModuleFactory.sol";
import "../interfaces/IModuleRegistry.sol";
import "../interfaces/IFeatureRegistry.sol";
import "../interfaces/ITransferManager.sol";
import "../modules/PermissionManager/IPermissionManager.sol";
import "../RegistryUpdater.sol";
import "../libraries/Util.sol";
import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol";
Expand Down Expand Up @@ -200,9 +201,9 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater
uint256 _granularity,
string memory _tokenDetails,
address _polymathRegistry
)
public
ERC20Detailed(_name, _symbol, _decimals) RegistryUpdater(_polymathRegistry)
)
public
ERC20Detailed(_name, _symbol, _decimals) RegistryUpdater(_polymathRegistry)
{
//When it is created, the owner is the STR
updateFromRegistry();
Expand All @@ -227,9 +228,9 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater
uint256 _maxCost,
uint256 _budget,
bytes32 _label
)
public
onlyOwner nonReentrant
)
public
onlyOwner nonReentrant
{
//Check that the module factory exists in the ModuleRegistry - will throw otherwise
IModuleRegistry(moduleRegistry).useModule(_moduleFactory);
Expand Down Expand Up @@ -571,10 +572,10 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater
uint256 _value,
bytes memory _data,
bool _isTransfer
)
internal
checkGranularity(_value)
returns(bool)
)
internal
checkGranularity(_value)
returns(bool)
{
if (!transfersFrozen) {
bool isInvalid = false;
Expand Down Expand Up @@ -648,11 +649,11 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater
address _investor,
uint256 _value,
bytes memory _data
)
public
onlyModuleOrOwner(MINT_KEY)
isMintingAllowed
returns(bool success)
)
public
onlyModuleOrOwner(MINT_KEY)
isMintingAllowed
returns(bool success)
{
require(_updateTransfer(address(0), _investor, _value, _data), "Transfer invalid");
_mint(_investor, _value);
Expand Down Expand Up @@ -686,12 +687,11 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater
*/
function checkPermission(address _delegate, address _module, bytes32 _perm) public view returns(bool) {
for (uint256 i = 0; i < modules[PERMISSION_KEY].length; i++) {
if (!modulesToData[modules[PERMISSION_KEY][i]].isArchived) return TokenLib.checkPermission(
modules[PERMISSION_KEY],
_delegate,
_module,
_perm
);
if (!modulesToData[modules[PERMISSION_KEY][i]].isArchived) {
if (IPermissionManager(modules[PERMISSION_KEY][i]).checkPermission(_delegate, _module, _perm)) {
return true;
}
}
}
return false;
}
Expand Down
Loading