diff --git a/contracts/modules/STO/USDTiered/USDTieredSTO.sol b/contracts/modules/STO/USDTiered/USDTieredSTO.sol index 67b2fc6f1..c042ee127 100644 --- a/contracts/modules/STO/USDTiered/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTiered/USDTieredSTO.sol @@ -190,6 +190,23 @@ contract USDTieredSTO is USDTieredSTOStorage, STO { _modifyAddresses(_wallet, _treasuryWallet, _usdTokens); } + /** + * @dev Modifies Oracle address. + * By default, Polymath oracles are used but issuer can overide them using this function + * Set _oracleAddress to 0x0 to fallback to using Polymath oracles + * @param _fundRaiseType Actual currency + * @param _oracleAddress address of the oracle + */ + function modifyOracle(FundRaiseType _fundRaiseType, address _oracleAddress) external { + _onlySecurityTokenOwner(); + if (_fundRaiseType == FundRaiseType.ETH) { + customOracles[bytes32("ETH")][bytes32("USD")] = _oracleAddress; + } else { + require(_fundRaiseType == FundRaiseType.POLY, "Invalid currency"); + customOracles[bytes32("POLY")][bytes32("USD")] = _oracleAddress; + } + } + function _modifyLimits(uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD) internal { minimumInvestmentUSD = _minimumInvestmentUSD; nonAccreditedLimitUSD = _nonAccreditedLimitUSD; @@ -757,8 +774,9 @@ contract USDTieredSTO is USDTieredSTOStorage, STO { return this.configure.selector; } - function _getOracle(bytes32 _currency, bytes32 _denominatedCurrency) internal view returns(address) { - return IPolymathRegistry(ISecurityToken(securityToken).polymathRegistry()).getAddress(oracleKeys[_currency][_denominatedCurrency]); + function _getOracle(bytes32 _currency, bytes32 _denominatedCurrency) internal view returns(address oracleAddress) { + oracleAddress = customOracles[_currency][_denominatedCurrency]; + if (oracleAddress == address(0)) + oracleAddress = IPolymathRegistry(ISecurityToken(securityToken).polymathRegistry()).getAddress(oracleKeys[_currency][_denominatedCurrency]); } - } diff --git a/contracts/modules/STO/USDTiered/USDTieredSTOStorage.sol b/contracts/modules/STO/USDTiered/USDTieredSTOStorage.sol index 75a1f6d1c..15cc553ae 100644 --- a/contracts/modules/STO/USDTiered/USDTieredSTOStorage.sol +++ b/contracts/modules/STO/USDTiered/USDTieredSTOStorage.sol @@ -74,4 +74,6 @@ contract USDTieredSTOStorage { // Array of Tiers Tier[] public tiers; + // Optional custom Oracles. + mapping(bytes32 => mapping(bytes32 => address)) customOracles; } diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 9148dfe7a..f7b014180 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -35,6 +35,8 @@ contract("USDTieredSTO", async (accounts) => { let ETH = 0; let POLY = 1; let DAI = 2; + let oldEthRate; + let oldPolyRate; let MESSAGE = "Transaction Should Fail!"; const GAS_PRICE = 0; @@ -909,6 +911,40 @@ contract("USDTieredSTO", async (accounts) => { }); describe("Test modifying configuration", async () => { + it("Should not allow unauthorized address to change oracle address", async () => { + let stoId = 3; + await catchRevert(I_USDTieredSTO_Array[stoId].modifyOracle(ETH, address_zero, { from: ACCREDITED1 })); + }); + + it("Should not allow to change oracle address for currencies other than ETH and POLY", async () => { + let stoId = 3; + await catchRevert(I_USDTieredSTO_Array[stoId].modifyOracle(DAI, address_zero, { from: ISSUER })); + }); + + it("Should allow to change oracle address for ETH", async () => { + let stoId = 3; + oldEthRate = await I_USDTieredSTO_Array[stoId].getRate.call(ETH); + let I_USDOracle2 = await MockOracle.new(address_zero, web3.utils.fromAscii("ETH"), web3.utils.fromAscii("USD"), e18, { from: POLYMATH }); + await I_USDTieredSTO_Array[stoId].modifyOracle(ETH, I_USDOracle2.address, { from: ISSUER }); + assert.equal((await I_USDTieredSTO_Array[stoId].getRate.call(ETH)).toString(), e18.toString()); + }); + + it("Should allow to change oracle address for POLY", async () => { + let stoId = 3; + oldPolyRate = await I_USDTieredSTO_Array[stoId].getRate.call(POLY); + let I_POLYOracle2 = await MockOracle.new(I_PolyToken.address, web3.utils.fromAscii("POLY"), web3.utils.fromAscii("USD"), e18, { from: POLYMATH }); + await I_USDTieredSTO_Array[stoId].modifyOracle(POLY, I_POLYOracle2.address, { from: ISSUER }); + assert.equal((await I_USDTieredSTO_Array[stoId].getRate.call(POLY)).toString(), e18.toString()); + }); + + it("Should use official oracles when custom oracle is set to 0x0", async () => { + let stoId = 3; + await I_USDTieredSTO_Array[stoId].modifyOracle(ETH, address_zero, { from: ISSUER }); + await I_USDTieredSTO_Array[stoId].modifyOracle(POLY, address_zero, { from: ISSUER }); + assert.equal((await I_USDTieredSTO_Array[stoId].getRate.call(ETH)).toString(), oldEthRate.toString()); + assert.equal((await I_USDTieredSTO_Array[stoId].getRate.call(POLY)).toString(), oldPolyRate.toString()); + }); + it("Should successfully change config before startTime - funding", async () => { let stoId = 3; await I_USDTieredSTO_Array[stoId].modifyFunding([0], { from: ISSUER });