diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index dfd7eca32..bfffbc535 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -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); @@ -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); } diff --git a/contracts/interfaces/IModuleFactory.sol b/contracts/interfaces/IModuleFactory.sol index 9af977287..8a988c3a3 100644 --- a/contracts/interfaces/IModuleFactory.sol +++ b/contracts/interfaces/IModuleFactory.sol @@ -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 diff --git a/contracts/interfaces/IOracle.sol b/contracts/interfaces/IOracle.sol index dc8205a2b..704814a5a 100644 --- a/contracts/interfaces/IOracle.sol +++ b/contracts/interfaces/IOracle.sol @@ -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); } diff --git a/contracts/libraries/TokenLib.sol b/contracts/libraries/TokenLib.sol index 8585a3dba..18567243f 100644 --- a/contracts/libraries/TokenLib.sol +++ b/contracts/libraries/TokenLib.sol @@ -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"; @@ -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 @@ -256,8 +231,8 @@ library TokenLib { uint256 _value, uint256 _balanceTo, uint256 _balanceFrom - ) - public + ) + public { if ((_value == 0) || (_from == _to)) { return; diff --git a/contracts/mocks/MockOracle.sol b/contracts/mocks/MockOracle.sol index 8208f8024..511c1ef5b 100644 --- a/contracts/mocks/MockOracle.sol +++ b/contracts/mocks/MockOracle.sol @@ -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; } diff --git a/contracts/modules/ModuleFactory.sol b/contracts/modules/ModuleFactory.sol index bc053e355..15e9f6cd9 100644 --- a/contracts/modules/ModuleFactory.sol +++ b/contracts/modules/ModuleFactory.sol @@ -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 @@ -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); } diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index bd64aaca6..18231229c 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -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) { @@ -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)); } @@ -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)); } diff --git a/contracts/oracles/MakerDAOOracle.sol b/contracts/oracles/MakerDAOOracle.sol index 85da5d7b6..5a390cf72 100644 --- a/contracts/oracles/MakerDAOOracle.sol +++ b/contracts/oracles/MakerDAOOracle.sol @@ -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; } diff --git a/contracts/oracles/PolyOracle.sol b/contracts/oracles/PolyOracle.sol index f25cd7971..5c7e4c856 100644 --- a/contracts/oracles/PolyOracle.sol +++ b/contracts/oracles/PolyOracle.sol @@ -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; diff --git a/contracts/oracles/StableOracle.sol b/contracts/oracles/StableOracle.sol new file mode 100644 index 000000000..aeb38b4af --- /dev/null +++ b/contracts/oracles/StableOracle.sol @@ -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); + } + +} diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 1bb6c9534..1351be50d 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -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"; @@ -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(); @@ -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); @@ -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; @@ -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); @@ -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; } diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index dae22700d..0cc03e0d5 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -25,6 +25,7 @@ const FeatureRegistry = artifacts.require("./FeatureRegistry.sol"); const STFactory = artifacts.require("./tokens/STFactory.sol"); const DevPolyToken = artifacts.require("./helpers/PolyTokenFaucet.sol"); const MockOracle = artifacts.require("./MockOracle.sol"); +const StableOracle = artifacts.require("./StableOracle.sol"); const TokenLib = artifacts.require("./TokenLib.sol"); const SecurityToken = artifacts.require("./tokens/SecurityToken.sol"); const STRGetter = artifacts.require('./STRGetter.sol'); @@ -44,6 +45,7 @@ let PolyToken; let UsdToken; let ETHOracle; let POLYOracle; +let StablePOLYOracle; module.exports = function(deployer, network, accounts) { // Ethereum account address hold by the Polymath (Act as the main account which have ownable permissions) @@ -68,11 +70,22 @@ module.exports = function(deployer, network, accounts) { web3.utils.fromAscii("USD"), new BN(5).mul(new BN(10).pow(new BN(17))), { from: PolymathAccount } - ) - .then(() => { - MockOracle.deployed().then(mockedOracle => { - POLYOracle = mockedOracle.address; - }); + ).then(() => { + return MockOracle.deployed(); + }).then(mockedOracle => { + POLYOracle = mockedOracle.address; + }).then(() => { + return deployer + .deploy( + StableOracle, + POLYOracle, + new BN(10).mul(new BN(10).pow(new BN(16))), + { from: PolymathAccount } + ); + }).then(() => { + return StableOracle.deployed(); + }).then(stableOracle => { + StablePOLYOracle = stableOracle.address; }); deployer .deploy( @@ -94,12 +107,14 @@ module.exports = function(deployer, network, accounts) { PolyToken = "0xb347b9f5b56b431b2cf4e1d90a5995f7519ca792"; // PolyToken Kovan Faucet Address POLYOracle = "0x461d98EF2A0c7Ac1416EF065840fF5d4C946206C"; // Poly Oracle Kovan Address ETHOracle = "0xCE5551FC9d43E9D2CC255139169FC889352405C8"; // ETH Oracle Kovan Address + StablePOLYOracle = ""; // TODO } else if (network === "mainnet") { web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/g5xfoQ0jFSE9S5LwM1Ei")); PolymathAccount = accounts[0]; PolyToken = "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC"; // Mainnet PolyToken Address POLYOracle = "0x52cb4616E191Ff664B0bff247469ce7b74579D1B"; // Poly Oracle Mainnet Address ETHOracle = "0x60055e9a93aae267da5a052e95846fa9469c0e7a"; // ETH Oracle Mainnet Address + StablePOLYOracle = ""; // TODO } if (network === "coverage") { web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); @@ -108,9 +123,21 @@ module.exports = function(deployer, network, accounts) { deployer .deploy(MockOracle, PolyToken, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), new BN(0.5).mul(new BN(10).pow(new BN(18))), { from: PolymathAccount }) .then(() => { - MockOracle.deployed().then(mockedOracle => { - POLYOracle = mockedOracle.address; - }); + return MockOracle.deployed(); + }).then(mockedOracle => { + POLYOracle = mockedOracle.address; + }).then(() => { + return deployer + .deploy( + StableOracle, + POLYOracle, + new BN(10).mul(new BN(10).pow(new BN(16))), + { from: PolymathAccount } + ) + }).then(() => { + return StableOracle.deployed(); + }).then(stableOracle => { + StablePOLYOracle = stableOracle.address; }); deployer.deploy(MockOracle, nullAddress, web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), new BN(500).mul(new BN(10).pow(new BN(18))), { from: PolymathAccount }).then(() => { MockOracle.deployed().then(mockedOracle => { @@ -491,6 +518,9 @@ module.exports = function(deployer, network, accounts) { .then(() => { return polymathRegistry.changeAddress("EthUsdOracle", ETHOracle, { from: PolymathAccount }); }) + .then(() => { + return polymathRegistry.changeAddress("StablePolyUsdOracle", StablePOLYOracle, { from: PolymathAccount }); + }) .then(() => { return deployer.deploy(SecurityToken, "a", "a", 18, 1, "a", polymathRegistry.address, { from: PolymathAccount }); }) @@ -507,6 +537,7 @@ module.exports = function(deployer, network, accounts) { ETHOracle: ${ETHOracle} POLYOracle: ${POLYOracle} + POLYStableOracle: ${StablePOLYOracle} STFactory: ${STFactory.address} GeneralTransferManagerLogic: ${GeneralTransferManagerLogic.address} diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 1c3a0fe6b..8adb680b8 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -2,6 +2,7 @@ import { encodeProxyCall, encodeModuleCall } from "./encodeCall"; const PolymathRegistry = artifacts.require("./PolymathRegistry.sol"); const MockOracle = artifacts.require("./MockOracle.sol"); +const StableOracle = artifacts.require("./StableOracle.sol"); const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); @@ -103,6 +104,7 @@ let I_STRGetter; let I_SignedTransferManagerFactory; let I_USDOracle; let I_POLYOracle; +let I_StablePOLYOracle; // Initial fee for ticker registry and security token registry const initRegFee = new BN(web3.utils.toWei("250")); @@ -147,7 +149,10 @@ export async function setUpPolymathNetwork(account_polymath, token_owner) { I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, I_STRProxied, - I_STRGetter + I_STRGetter, + I_USDOracle, + I_POLYOracle, + I_StablePOLYOracle ); return Promise.all(tempArray); } @@ -155,10 +160,13 @@ export async function setUpPolymathNetwork(account_polymath, token_owner) { export async function addOracles(account_polymath) { let USDETH = new BN(500).mul(new BN(10).pow(new BN(18))); // 500 USD/ETH let USDPOLY = new BN(25).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY + let StableChange = new BN(10).mul(new BN(10).pow(new BN(16))); // 0.25 USD/POLY I_USDOracle = await MockOracle.new("0x0000000000000000000000000000000000000000", web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), USDETH, { from: account_polymath }); // 500 dollars per POLY I_POLYOracle = await MockOracle.new(I_PolyToken.address, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), USDPOLY, { from: account_polymath }); // 25 cents per POLY + I_StablePOLYOracle = await StableOracle.new(I_POLYOracle.address, StableChange, { from: account_polymath }); await I_PolymathRegistry.changeAddress("EthUsdOracle", I_USDOracle.address, { from: account_polymath }); await I_PolymathRegistry.changeAddress("PolyUsdOracle", I_POLYOracle.address, { from: account_polymath }); + await I_PolymathRegistry.changeAddress("StablePolyUsdOracle", I_StablePOLYOracle.address, { from: account_polymath }); } export async function deployPolyRegistryAndPolyToken(account_polymath, token_owner) { diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index a7d19c13c..fd2fbeb7e 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -61,6 +61,9 @@ contract("SecurityTokenRegistry", async (accounts) => { let I_MRProxied; let I_STRGetter; let I_Getter; + let I_USDOracle; + let I_POLYOracle; + let I_StablePOLYOracle; // SecurityToken Details (Launched ST on the behalf of the issuer) const name = "Demo Token"; @@ -120,7 +123,10 @@ contract("SecurityTokenRegistry", async (accounts) => { I_SecurityTokenRegistry, I_SecurityTokenRegistryProxy, I_STRProxied, - I_STRGetter + I_STRGetter, + I_USDOracle, + I_POLYOracle, + I_StablePOLYOracle ] = instances; // STEP 8: Deploy the CappedSTOFactory @@ -357,6 +363,59 @@ contract("SecurityTokenRegistry", async (accounts) => { assert.equal(data[3], ""); }); + it("Should change ticker price based on oracle", async () => { + let snap_Id = await takeSnapshot(); + let origPriceUSD = new BN(web3.utils.toWei("250")); + let origPricePOLY = new BN(web3.utils.toWei("1000")); + let currentRate = await I_POLYOracle.getPrice.call(); + console.log("Current Rate: " + currentRate); + let feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + let feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), origPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), origPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(27).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // No change as difference is less than 10% + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), origPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), origPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(20).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + let newPricePOLY = new BN(web3.utils.toWei("1250")); + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), newPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), newPricePOLY.toString()); + await I_POLYOracle.changePrice(new BN(21).mul(new BN(10).pow(new BN(16)))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // No change as difference is less than 10% + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), newPricePOLY.toString()); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), newPricePOLY.toString()); + await I_StablePOLYOracle.changeEvictPercentage(new BN(10).pow(new BN(16))); + await I_STRProxied.getFees("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesTicker = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + feesToken = await I_STRProxied.getFees.call("0x2fcc69711628630fb5a42566c68bd1092bc4aa26826736293969fddcd11cb2d2"); + // Change as eviction percentage updated + // newPricePOLY = new BN(web3.utils.toWei("1250")); + //1190.476190476190476190 = 250/0.21 + assert.equal(feesTicker[0].toString(), origPriceUSD.toString()); + assert.equal(feesTicker[1].toString(), "1190476190476190476190"); + assert.equal(feesToken[0].toString(), origPriceUSD.toString()); + assert.equal(feesToken[1].toString(), "1190476190476190476190"); + await revertToSnapshot(snap_Id); + }); + it("Should register the ticker when the tickerRegFee is 0", async () => { let snap_Id = await takeSnapshot(); await I_STRProxied.changeTickerRegistrationFee(0, { from: account_polymath }); diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 7946512a8..1da3d9f20 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -179,18 +179,18 @@ contract("USDTieredSTO", async (accounts) => { .div(e18); if (_currencyTo == "USD") return tokenToUSD; if (_currencyTo == "ETH") { - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(ETH, tokenToUSD); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(ETH, tokenToUSD); } else if (_currencyTo == "POLY") { - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(POLY, tokenToUSD); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(POLY, tokenToUSD); } } if (_currencyFrom == "USD") { if (_currencyTo == "TOKEN") return _amount.div(USDTOKEN).mul(e18); // USD / USD/TOKEN = TOKEN if (_currencyTo == "ETH" || _currencyTo == "POLY") - return await I_USDTieredSTO_Array[_stoID].convertFromUSD(_currencyTo == "ETH" ? ETH : POLY, _amount); + return await I_USDTieredSTO_Array[_stoID].convertFromUSD.call(_currencyTo == "ETH" ? ETH : POLY, _amount); } if (_currencyFrom == "ETH" || _currencyFrom == "POLY") { - let ethToUSD = await I_USDTieredSTO_Array[_stoID].convertToUSD(_currencyTo == "ETH" ? ETH : POLY, _amount); + let ethToUSD = await I_USDTieredSTO_Array[_stoID].convertToUSD.call(_currencyTo == "ETH" ? ETH : POLY, _amount); if (_currencyTo == "USD") return ethToUSD; if (_currencyTo == "TOKEN") return ethToUSD.div(USDTOKEN).mul(e18); // USD / USD/TOKEN = TOKEN } @@ -4596,7 +4596,7 @@ contract("USDTieredSTO", async (accounts) => { it("should get the right conversion for ETH to USD", async () => { // 20 ETH to 10000 USD let ethInWei = new BN(web3.utils.toWei("20", "ether")); - let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD(ETH, ethInWei); + let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD.call(ETH, ethInWei); assert.equal( usdInWei.div(e18).toString(), ethInWei @@ -4609,7 +4609,7 @@ contract("USDTieredSTO", async (accounts) => { it("should get the right conversion for POLY to USD", async () => { // 40000 POLY to 10000 USD let polyInWei = new BN(web3.utils.toWei("40000", "ether")); - let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD(POLY, polyInWei); + let usdInWei = await I_USDTieredSTO_Array[0].convertToUSD.call(POLY, polyInWei); assert.equal( usdInWei.toString(), polyInWei @@ -4624,7 +4624,7 @@ contract("USDTieredSTO", async (accounts) => { it("should get the right conversion for USD to ETH", async () => { // 10000 USD to 20 ETH let usdInWei = new BN(web3.utils.toWei("10000", "ether")); - let ethInWei = await I_USDTieredSTO_Array[0].convertFromUSD(ETH, usdInWei); + let ethInWei = await I_USDTieredSTO_Array[0].convertFromUSD.call(ETH, usdInWei); assert.equal( ethInWei.div(e18).toString(), usdInWei @@ -4637,7 +4637,7 @@ contract("USDTieredSTO", async (accounts) => { it("should get the right conversion for USD to POLY", async () => { // 10000 USD to 40000 POLY let usdInWei = new BN(web3.utils.toWei("10000", "ether")); - let polyInWei = await I_USDTieredSTO_Array[0].convertFromUSD(POLY, usdInWei); + let polyInWei = await I_USDTieredSTO_Array[0].convertFromUSD.call(POLY, usdInWei); assert.equal( polyInWei.toString(), usdInWei.mul(e18).div(USDPOLY).toString()