From 493aed65f66e41b32949d72ff909542d44fca7e2 Mon Sep 17 00:00:00 2001 From: evgenipirianov Date: Mon, 6 Jan 2020 15:48:18 +0100 Subject: [PATCH 01/11] added changes to the 3 tickets --- .../contractkit/src/wrappers/SortedOracles.ts | 8 +- .../contracts/common/GasPriceMinimum.sol | 6 +- .../protocol/contracts/common/GoldToken.sol | 13 +- .../protocol/contracts/common/MultiSig.sol | 19 ++ .../contracts/governance/EpochRewards.sol | 29 ++- .../contracts/governance/Governance.sol | 46 +++-- .../contracts/governance/LockedGold.sol | 4 +- .../protocol/contracts/stability/Exchange.sol | 11 +- .../protocol/contracts/stability/Reserve.sol | 4 +- .../contracts/stability/SortedOracles.sol | 45 +++-- .../contracts/stability/StableToken.sol | 84 +++++++- .../stability/interfaces/ISortedOracles.sol | 2 +- .../protocol/migrations/08_stabletoken.ts | 7 +- .../scripts/truffle/set_exchange_rate.ts | 7 +- .../protocol/test/stability/sortedoracles.ts | 180 +++++++++++++----- 15 files changed, 356 insertions(+), 109 deletions(-) diff --git a/packages/contractkit/src/wrappers/SortedOracles.ts b/packages/contractkit/src/wrappers/SortedOracles.ts index 56657d08248..fe8d39abd2c 100644 --- a/packages/contractkit/src/wrappers/SortedOracles.ts +++ b/packages/contractkit/src/wrappers/SortedOracles.ts @@ -98,9 +98,13 @@ export class SortedOraclesWrapper extends BaseWrapper { oracleAddress ) + const reportedValue = toBigNumber(numerator.toString()) + .dividedBy(toBigNumber(denominator.toString())) + .multipliedBy(await this.getInternalDenominator()) + return toTransactionObject( this.kit, - this.contract.methods.report(tokenAddress, numerator, denominator, lesserKey, greaterKey), + this.contract.methods.report(tokenAddress, reportedValue, lesserKey, greaterKey), { from: oracleAddress } ) } @@ -158,7 +162,7 @@ export class SortedOraclesWrapper extends BaseWrapper { } private async getInternalDenominator(): Promise { - return toBigNumber(await this.contract.methods.DENOMINATOR().call()) + return toBigNumber(await this.contract.methods.getDenominator().call()) } private async findLesserAndGreaterKeys( diff --git a/packages/protocol/contracts/common/GasPriceMinimum.sol b/packages/protocol/contracts/common/GasPriceMinimum.sol index f391f182337..27c0264d1f7 100644 --- a/packages/protocol/contracts/common/GasPriceMinimum.sol +++ b/packages/protocol/contracts/common/GasPriceMinimum.sol @@ -82,7 +82,11 @@ contract GasPriceMinimum is Ownable, Initializable, UsingRegistry { uint256 rateNumerator; uint256 rateDenominator; (rateNumerator, rateDenominator) = sortedOracles.medianRate(tokenAddress); - return (gasPriceMinimum.mul(rateNumerator).div(rateDenominator)); + FixidityLib.Fraction memory ratio = FixidityLib.newFixedFraction( + rateNumerator, + rateDenominator + ); + return (FixidityLib.newFixed(gasPriceMinimum)).multiply(ratio).fromFixed(); } } diff --git a/packages/protocol/contracts/common/GoldToken.sol b/packages/protocol/contracts/common/GoldToken.sol index 4b24f2e269e..0adee8ab99b 100644 --- a/packages/protocol/contracts/common/GoldToken.sol +++ b/packages/protocol/contracts/common/GoldToken.sol @@ -26,6 +26,8 @@ contract GoldToken is Initializable, IERC20Token, ICeloToken { event Approval(address indexed owner, address indexed spender, uint256 value); + event TotalSupplySet(uint256 totalSupply); + /** * Only VM would be able to set the caller address to 0x0 unless someone * really has the private key for 0x0 @@ -40,7 +42,16 @@ contract GoldToken is Initializable, IERC20Token, ICeloToken { */ // solhint-disable-next-line no-empty-blocks function initialize() external initializer { - totalSupply_ = 0; + setInitialTotalSupply(0); + } + + /** + * @notice Sets the token initial supply + * @param _totalSupply new token initial supply. + */ + function setInitialTotalSupply(uint256 _totalSupply) private { + totalSupply_ = _totalSupply; + emit TotalSupplySet(_totalSupply); } /** diff --git a/packages/protocol/contracts/common/MultiSig.sol b/packages/protocol/contracts/common/MultiSig.sol index a9a7bd48a77..3d1c1a05a59 100644 --- a/packages/protocol/contracts/common/MultiSig.sol +++ b/packages/protocol/contracts/common/MultiSig.sol @@ -20,6 +20,7 @@ contract MultiSig is Initializable { event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event RequirementChange(uint256 required); + event RequirementSet(uint256 required); /* * Constants @@ -109,6 +110,15 @@ contract MultiSig is Initializable { initializer validRequirement(_owners.length, _required) { + setInitialOwners(_owners); + setInitialRequired(_required); + } + + /** + * @notice Sets the initial owners of the wallet. + * @param _owners List of initial owners. + */ + function setInitialOwners(address[] memory _owners) private { for (uint256 i = 0; i < _owners.length; i++) { require( !isOwner[_owners[i]] && _owners[i] != address(0), @@ -117,7 +127,16 @@ contract MultiSig is Initializable { isOwner[_owners[i]] = true; } owners = _owners; + } + + /** + * @notice Sets the required number of tx confirmations. + * @param _required The number of required tx confirmations. + */ + function setInitialRequired(uint256 _required) private { + require(_required > 0, "Required confirmations must be greater than zero"); required = _required; + emit RequirementSet(_required); } /// @dev Allows to add a new owner. Transaction has to be sent by wallet. diff --git a/packages/protocol/contracts/governance/EpochRewards.sol b/packages/protocol/contracts/governance/EpochRewards.sol index 2bf40d6e2b7..91e287ea149 100644 --- a/packages/protocol/contracts/governance/EpochRewards.sol +++ b/packages/protocol/contracts/governance/EpochRewards.sol @@ -64,6 +64,8 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry uint256 underspendAdjustmentFactor, uint256 overspendAdjustmentFactor ); + event StartTimeSet(uint256 startTime); + event TargetVotingYield(uint256 targetVotingYield); /** * @notice Initializes critical variables. @@ -101,8 +103,8 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry ); setTargetVotingGoldFraction(_targetVotingGoldFraction); setTargetValidatorEpochPayment(_targetValidatorEpochPayment); - targetVotingYieldParams.target = FixidityLib.wrap(targetVotingYieldInitial); - startTime = now; + setTargetVotingYield(targetVotingYieldInitial); + setStartTime(block.timestamp); } /** @@ -159,6 +161,29 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry return true; } + /** + * @notice Sets the target voting yield for validators. + * @param _targetVotingYield The value of the target voting yield. + * @return True upon success. + */ + function setTargetVotingYield(uint256 _targetVotingYield) public onlyOwner returns (bool) { + targetVotingYieldParams.target = FixidityLib.wrap(_targetVotingYield); + emit TargetVotingYield(_targetVotingYield); + return true; + } + + /** + * @notice Sets the start time. + * @param _startTime The time in unix. + * @return True upon success. + */ + function setStartTime(uint256 _startTime) public onlyOwner returns (bool) { + require(_startTime >= block.timestamp, "The start time must not be in the past"); + startTime = _startTime; + emit StartTimeSet(_startTime); + return true; + } + /** * @notice Sets the rewards multiplier parameters. * @param max The max multiplier on target epoch rewards. diff --git a/packages/protocol/contracts/governance/Governance.sol b/packages/protocol/contracts/governance/Governance.sol index 850a1dcb464..940d508bf24 100644 --- a/packages/protocol/contracts/governance/Governance.sol +++ b/packages/protocol/contracts/governance/Governance.sol @@ -118,6 +118,8 @@ contract Governance is event ExecutionStageDurationSet(uint256 executionStageDuration); + event LastDequeueSet(uint256 lastDequeue); + event ConstitutionSet(address indexed destination, bytes4 indexed functionId, uint256 threshold); event ProposalQueued( @@ -217,27 +219,37 @@ contract Governance is ); _transferOwnership(msg.sender); setRegistry(registryAddress); - approver = _approver; - concurrentProposals = _concurrentProposals; - minDeposit = _minDeposit; - queueExpiry = _queueExpiry; - dequeueFrequency = _dequeueFrequency; - stageDurations.approval = approvalStageDuration; - stageDurations.referendum = referendumStageDuration; - stageDurations.execution = executionStageDuration; + setApprover(_approver); + setConcurrentProposals(_concurrentProposals); + setMinDeposit(_minDeposit); + setQueueExpiry(_queueExpiry); + setDequeueFrequency(_dequeueFrequency); + setApprovalStageDuration(approvalStageDuration); + setReferendumStageDuration(referendumStageDuration); + setExecutionStageDuration(executionStageDuration); setParticipationBaseline(participationBaseline); setParticipationFloor(participationFloor); setBaselineUpdateFactor(baselineUpdateFactor); setBaselineQuorumFactor(baselineQuorumFactor); + setLastDequeue(block.timestamp); + } + + /** + * @notice Updates the last dequeue timestamp. + * @param _lastDequeue The last dequeue timestamp. + */ + function setLastDequeue(uint256 _lastDequeue) public onlyOwner { + require(_lastDequeue >= block.timestamp, "last dequeuing time must not be in the past"); // solhint-disable-next-line not-rely-on-time - lastDequeue = now; + lastDequeue = _lastDequeue; + emit LastDequeueSet(_lastDequeue); } /** * @notice Updates the address that has permission to approve proposals in the approval stage. * @param _approver The address that has permission to approve proposals in the approval stage. */ - function setApprover(address _approver) external onlyOwner { + function setApprover(address _approver) public onlyOwner { require(_approver != address(0) && _approver != approver); approver = _approver; emit ApproverSet(_approver); @@ -247,7 +259,7 @@ contract Governance is * @notice Updates the number of proposals to dequeue at a time. * @param _concurrentProposals The number of proposals to dequeue at at a time. */ - function setConcurrentProposals(uint256 _concurrentProposals) external onlyOwner { + function setConcurrentProposals(uint256 _concurrentProposals) public onlyOwner { require(_concurrentProposals > 0 && _concurrentProposals != concurrentProposals); concurrentProposals = _concurrentProposals; emit ConcurrentProposalsSet(_concurrentProposals); @@ -257,7 +269,7 @@ contract Governance is * @notice Updates the minimum deposit needed to make a proposal. * @param _minDeposit The minimum Celo Gold deposit needed to make a proposal. */ - function setMinDeposit(uint256 _minDeposit) external onlyOwner { + function setMinDeposit(uint256 _minDeposit) public onlyOwner { require(_minDeposit != minDeposit); minDeposit = _minDeposit; emit MinDepositSet(_minDeposit); @@ -267,7 +279,7 @@ contract Governance is * @notice Updates the number of seconds before a queued proposal expires. * @param _queueExpiry The number of seconds a proposal can stay in the queue before expiring. */ - function setQueueExpiry(uint256 _queueExpiry) external onlyOwner { + function setQueueExpiry(uint256 _queueExpiry) public onlyOwner { require(_queueExpiry > 0 && _queueExpiry != queueExpiry); queueExpiry = _queueExpiry; emit QueueExpirySet(_queueExpiry); @@ -279,7 +291,7 @@ contract Governance is * @param _dequeueFrequency The number of seconds before the next batch of proposals can be * dequeued. */ - function setDequeueFrequency(uint256 _dequeueFrequency) external onlyOwner { + function setDequeueFrequency(uint256 _dequeueFrequency) public onlyOwner { require(_dequeueFrequency > 0 && _dequeueFrequency != dequeueFrequency); dequeueFrequency = _dequeueFrequency; emit DequeueFrequencySet(_dequeueFrequency); @@ -289,7 +301,7 @@ contract Governance is * @notice Updates the number of seconds proposals stay in the approval stage. * @param approvalStageDuration The number of seconds proposals stay in the approval stage. */ - function setApprovalStageDuration(uint256 approvalStageDuration) external onlyOwner { + function setApprovalStageDuration(uint256 approvalStageDuration) public onlyOwner { require(approvalStageDuration > 0 && approvalStageDuration != stageDurations.approval); stageDurations.approval = approvalStageDuration; emit ApprovalStageDurationSet(approvalStageDuration); @@ -299,7 +311,7 @@ contract Governance is * @notice Updates the number of seconds proposals stay in the referendum stage. * @param referendumStageDuration The number of seconds proposals stay in the referendum stage. */ - function setReferendumStageDuration(uint256 referendumStageDuration) external onlyOwner { + function setReferendumStageDuration(uint256 referendumStageDuration) public onlyOwner { require(referendumStageDuration > 0 && referendumStageDuration != stageDurations.referendum); stageDurations.referendum = referendumStageDuration; emit ReferendumStageDurationSet(referendumStageDuration); @@ -309,7 +321,7 @@ contract Governance is * @notice Updates the number of seconds proposals stay in the execution stage. * @param executionStageDuration The number of seconds proposals stay in the execution stage. */ - function setExecutionStageDuration(uint256 executionStageDuration) external onlyOwner { + function setExecutionStageDuration(uint256 executionStageDuration) public onlyOwner { require(executionStageDuration > 0 && executionStageDuration != stageDurations.execution); stageDurations.execution = executionStageDuration; emit ExecutionStageDurationSet(executionStageDuration); diff --git a/packages/protocol/contracts/governance/LockedGold.sol b/packages/protocol/contracts/governance/LockedGold.sol index 58b25a0f61c..26bbb0205c3 100644 --- a/packages/protocol/contracts/governance/LockedGold.sol +++ b/packages/protocol/contracts/governance/LockedGold.sol @@ -65,14 +65,14 @@ contract LockedGold is ILockedGold, ReentrancyGuard, Initializable, UsingRegistr function initialize(address registryAddress, uint256 _unlockingPeriod) external initializer { _transferOwnership(msg.sender); setRegistry(registryAddress); - unlockingPeriod = _unlockingPeriod; + setUnlockingPeriod(_unlockingPeriod); } /** * @notice Sets the duration in seconds users must wait before withdrawing gold after unlocking. * @param value The unlocking period in seconds. */ - function setUnlockingPeriod(uint256 value) external onlyOwner { + function setUnlockingPeriod(uint256 value) public onlyOwner { require(value != unlockingPeriod); unlockingPeriod = value; emit UnlockingPeriodSet(value); diff --git a/packages/protocol/contracts/stability/Exchange.sol b/packages/protocol/contracts/stability/Exchange.sol index 016be3d51e9..d2d079d2b4b 100644 --- a/packages/protocol/contracts/stability/Exchange.sol +++ b/packages/protocol/contracts/stability/Exchange.sol @@ -7,7 +7,6 @@ import "./interfaces/IExchange.sol"; import "./interfaces/ISortedOracles.sol"; import "./interfaces/IReserve.sol"; import "./interfaces/IStableToken.sol"; -import "../common/FractionUtil.sol"; import "../common/Initializable.sol"; import "../common/FixidityLib.sol"; import "../baklava/Freezable.sol"; @@ -19,7 +18,6 @@ import "../common/UsingRegistry.sol"; */ contract Exchange is IExchange, Initializable, Ownable, UsingRegistry, ReentrancyGuard, Freezable { using SafeMath for uint256; - using FractionUtil for FractionUtil.Fraction; using FixidityLib for FixidityLib.Fraction; event Exchanged(address indexed exchanger, uint256 sellAmount, uint256 buyAmount, bool soldGold); @@ -295,7 +293,9 @@ contract Exchange is IExchange, Initializable, Ownable, UsingRegistry, Reentranc function getUpdatedBuckets() private view returns (uint256, uint256) { uint256 updatedGoldBucket = getUpdatedGoldBucket(); - uint256 updatedStableBucket = getOracleExchangeRate().mul(updatedGoldBucket); + uint256 updatedStableBucket = getOracleExchangeRate() + .multiply(FixidityLib.newFixed(updatedGoldBucket)) + .fromFixed(); return (updatedGoldBucket, updatedStableBucket); } @@ -350,13 +350,14 @@ contract Exchange is IExchange, Initializable, Ownable, UsingRegistry, Reentranc return timePassed && enoughReports && medianReportRecent; } - function getOracleExchangeRate() private view returns (FractionUtil.Fraction memory) { + function getOracleExchangeRate() private view returns (FixidityLib.Fraction memory) { uint256 rateNumerator; uint256 rateDenominator; (rateNumerator, rateDenominator) = ISortedOracles( registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID) ) .medianRate(stable); - return FractionUtil.Fraction(rateNumerator, rateDenominator); + + return FixidityLib.newFixedFraction(rateNumerator, rateDenominator); } } diff --git a/packages/protocol/contracts/stability/Reserve.sol b/packages/protocol/contracts/stability/Reserve.sol index 3c82ad7424b..e37571533ac 100644 --- a/packages/protocol/contracts/stability/Reserve.sol +++ b/packages/protocol/contracts/stability/Reserve.sol @@ -48,14 +48,14 @@ contract Reserve is IReserve, Ownable, Initializable, UsingRegistry, ReentrancyG { _transferOwnership(msg.sender); setRegistry(registryAddress); - tobinTaxStalenessThreshold = _tobinTaxStalenessThreshold; + setTobinTaxStalenessThreshold(_tobinTaxStalenessThreshold); } /** * @notice Sets the number of seconds to cache the tobin tax value for. * @param value The number of seconds to cache the tobin tax value for. */ - function setTobinTaxStalenessThreshold(uint256 value) external onlyOwner { + function setTobinTaxStalenessThreshold(uint256 value) public onlyOwner { require(value > 0, "value was zero"); tobinTaxStalenessThreshold = value; emit TobinTaxStalenessThresholdSet(value); diff --git a/packages/protocol/contracts/stability/SortedOracles.sol b/packages/protocol/contracts/stability/SortedOracles.sol index f4429d57e72..0835d191e76 100644 --- a/packages/protocol/contracts/stability/SortedOracles.sol +++ b/packages/protocol/contracts/stability/SortedOracles.sol @@ -6,6 +6,7 @@ import "./interfaces/ISortedOracles.sol"; import "../common/Initializable.sol"; import "../common/linkedlists/AddressSortedLinkedListWithMedian.sol"; import "../common/linkedlists/SortedLinkedListWithMedian.sol"; +import "../common/FixidityLib.sol"; // TODO: Move SortedOracles to Fixidity /** @@ -14,8 +15,10 @@ import "../common/linkedlists/SortedLinkedListWithMedian.sol"; contract SortedOracles is ISortedOracles, Ownable, Initializable { using SafeMath for uint256; using AddressSortedLinkedListWithMedian for SortedLinkedListWithMedian.List; + using FixidityLib for FixidityLib.Fraction; + // All oracle rates are assumed to have a denominator of 2 ^ 64. - uint256 public constant DENOMINATOR = 0x10000000000000000; + uint256 private constant DENOMINATOR = 0x10000000000000000; // Maps a token address to a sorted list of report values. mapping(address => SortedLinkedListWithMedian.List) private rates; @@ -51,14 +54,14 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { function initialize(uint256 _reportExpirySeconds) external initializer { _transferOwnership(msg.sender); - reportExpirySeconds = _reportExpirySeconds; + setReportExpiry(_reportExpirySeconds); } /** * @notice Sets the report expiry parameter. * @param _reportExpirySeconds Desired value of report expiry. */ - function setReportExpiry(uint256 _reportExpirySeconds) external onlyOwner { + function setReportExpiry(uint256 _reportExpirySeconds) public onlyOwner { reportExpirySeconds = _reportExpirySeconds; emit ReportExpirySet(_reportExpirySeconds); } @@ -121,23 +124,19 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { /** * @notice Updates an oracle value and the median. * @param token The address of the token for which the Celo Gold exchange rate is being reported. - * @param numerator The amount of tokens equal to `denominator` Celo Gold. - * @param denominator The amount of Celo Gold equal to `numerator` tokens. + * @param value The amount of tokens equal to Celo Gold represented as a FixidityLib fraction * @param lesserKey The element which should be just left of the new oracle value. * @param greaterKey The element which should be just right of the new oracle value. * @dev Note that only one of `lesserKey` or `greaterKey` needs to be correct to reduce friction. */ - function report( - address token, - uint256 numerator, - uint256 denominator, - address lesserKey, - address greaterKey - ) external onlyOracle(token) { + function report(address token, uint256 value, address lesserKey, address greaterKey) + external + onlyOracle(token) + { uint256 originalMedian = rates[token].getMedianValue(); - uint256 value = numerator.mul(DENOMINATOR).div(denominator); + FixidityLib.Fraction memory fractionalValueReported = FixidityLib.wrap(value); if (rates[token].contains(msg.sender)) { - rates[token].update(msg.sender, value, lesserKey, greaterKey); + rates[token].update(msg.sender, fractionalValueReported.unwrap(), lesserKey, greaterKey); // Rather than update the timestamp, we remove it and re-add it at the // head of the list later. The reason for this is that we need to handle @@ -151,7 +150,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { // does the right thing in all cases. timestamps[token].remove(msg.sender); } else { - rates[token].insert(msg.sender, value, lesserKey, greaterKey); + rates[token].insert(msg.sender, fractionalValueReported.unwrap(), lesserKey, greaterKey); } timestamps[token].insert( msg.sender, @@ -160,13 +159,21 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { timestamps[token].getHead(), address(0) ); - emit OracleReported(token, msg.sender, now, value, DENOMINATOR); + emit OracleReported(token, msg.sender, now, fractionalValueReported.unwrap(), getDenominator()); uint256 newMedian = rates[token].getMedianValue(); if (newMedian != originalMedian) { - emit MedianUpdated(token, newMedian, DENOMINATOR); + emit MedianUpdated(token, newMedian, getDenominator()); } } + /** + * @notice Returns the common fixidity denominator. + * @return The common fixidity denominator. + */ + function getDenominator() public view returns (uint256) { + return DENOMINATOR; + } + /** * @notice Returns the number of rates. * @param token The address of the token for which the Celo Gold exchange rate is being reported. @@ -182,7 +189,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { * @return The median exchange rate for `token`. */ function medianRate(address token) external view returns (uint256, uint256) { - return (rates[token].getMedianValue(), numRates(token) == 0 ? 0 : DENOMINATOR); + return (rates[token].getMedianValue(), numRates(token) == 0 ? 0 : getDenominator()); } /** @@ -251,7 +258,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { emit OracleReportRemoved(token, oracle); uint256 newMedian = rates[token].getMedianValue(); if (newMedian != originalMedian) { - emit MedianUpdated(token, newMedian, numRates(token) == 0 ? 0 : DENOMINATOR); + emit MedianUpdated(token, newMedian, numRates(token) == 0 ? 0 : getDenominator()); } } } diff --git a/packages/protocol/contracts/stability/StableToken.sol b/packages/protocol/contracts/stability/StableToken.sol index 55b7a7de84f..6066720fa81 100644 --- a/packages/protocol/contracts/stability/StableToken.sol +++ b/packages/protocol/contracts/stability/StableToken.sol @@ -35,6 +35,14 @@ contract StableToken is event TransferComment(string comment); + event TotalSupplySet(address sender, uint256 totalSupply); + + event TokenNameSet(address sender, string name); + + event TokenSymbolSet(address sender, string symbol); + + event TokenDecimalslSet(address sender, uint8 decimals); + string internal name_; string internal symbol_; uint8 internal decimals_; @@ -109,16 +117,11 @@ contract StableToken is require(inflationRate != 0, "Must provide a non-zero inflation rate."); _transferOwnership(msg.sender); - totalSupply_ = 0; - name_ = _name; - symbol_ = _symbol; - decimals_ = _decimals; - - inflationState.rate = FixidityLib.wrap(inflationRate); - inflationState.factor = FixidityLib.fixed1(); - inflationState.updatePeriod = inflationFactorUpdatePeriod; - // solhint-disable-next-line not-rely-on-time - inflationState.factorLastUpdated = now; + setTotalSupply(0); + setName(_name); + setSymbol(_symbol); + setDecimals(_decimals); + setInitialInflationParameters(inflationRate, inflationFactorUpdatePeriod); require(initialBalanceAddresses.length == initialBalanceValues.length); for (uint256 i = 0; i < initialBalanceAddresses.length; i = i.add(1)) { @@ -127,6 +130,67 @@ contract StableToken is setRegistry(registryAddress); } + /** + * @notice Setter for the total supply. + * @param totalSupply The total supply to set. + */ + function setTotalSupply(uint256 totalSupply) public { + totalSupply_ = totalSupply; + emit TotalSupplySet(msg.sender, totalSupply); + } + + /** + * @notice Setter for the name of the token. + * @param name The name to set. + */ + function setName(string memory name) public { + name_ = name; + emit TokenNameSet(msg.sender, name); + } + + /** + * @notice Setter for the symbol of the token. + * @param symbol The symbol to set. + */ + function setSymbol(string memory symbol) public { + symbol_ = symbol; + emit TokenSymbolSet(msg.sender, symbol); + } + + /** + * @notice Setter for the decimals of the token. + * @param decimals The decimals to set. + */ + function setDecimals(uint8 decimals) public { + decimals_ = decimals; + emit TokenDecimalslSet(msg.sender, decimals); + } + + /** + * @notice Sets initial inflation Parameters. + * @param inflationRate new rate. + * @param inflationFactorUpdatePeriod how often inflationFactor is updated. + */ + function setInitialInflationParameters(uint256 inflationRate, uint256 inflationFactorUpdatePeriod) + private + { + require(inflationRate != 0, "Must provide a non-zero inflation rate."); + inflationState.rate = FixidityLib.wrap(inflationRate); + inflationState.updatePeriod = inflationFactorUpdatePeriod; + + emit InflationParametersUpdated( + inflationRate, + inflationFactorUpdatePeriod, + // solhint-disable-next-line not-rely-on-time + now + ); + + inflationState.factor = FixidityLib.fixed1(); + inflationState.factorLastUpdated = now; + + emit InflationFactorUpdated(inflationState.factor.unwrap(), inflationState.factorLastUpdated); + } + /** * @notice Updates Inflation Parameters. * @param rate new rate. diff --git a/packages/protocol/contracts/stability/interfaces/ISortedOracles.sol b/packages/protocol/contracts/stability/interfaces/ISortedOracles.sol index f388117fb37..ba617cb19e5 100644 --- a/packages/protocol/contracts/stability/interfaces/ISortedOracles.sol +++ b/packages/protocol/contracts/stability/interfaces/ISortedOracles.sol @@ -3,7 +3,7 @@ pragma solidity ^0.5.3; interface ISortedOracles { function addOracle(address, address) external; function removeOracle(address, address, uint256) external; - function report(address, uint256, uint256, address, address) external; + function report(address, uint256, address, address) external; function removeExpiredReports(address, uint256) external; function numRates(address) external view returns (uint256); function medianRate(address) external view returns (uint256, uint256); diff --git a/packages/protocol/migrations/08_stabletoken.ts b/packages/protocol/migrations/08_stabletoken.ts index 8d45be380c9..895f565845a 100644 --- a/packages/protocol/migrations/08_stabletoken.ts +++ b/packages/protocol/migrations/08_stabletoken.ts @@ -8,6 +8,7 @@ import { } from '@celo/protocol/lib/web3-utils' import { config } from '@celo/protocol/migrationsConfig' import { toFixed } from '@celo/utils/lib/fixidity' +import BigNumber from 'bignumber.js' import { FeeCurrencyWhitelistInstance, ReserveInstance, @@ -55,10 +56,12 @@ module.exports = deploymentForCoreContract( if (!(await sortedOracles.isOracle(stableToken.address, minerAddress))) { await sortedOracles.addOracle(stableToken.address, minerAddress) } + await sortedOracles.report( stableToken.address, - config.stableToken.goldPrice, - 1, + new BigNumber(config.stableToken.goldPrice / 1).multipliedBy( + await sortedOracles.getDenominator() + ), NULL_ADDRESS, NULL_ADDRESS ) diff --git a/packages/protocol/scripts/truffle/set_exchange_rate.ts b/packages/protocol/scripts/truffle/set_exchange_rate.ts index 699383cce2d..49c1667e66a 100644 --- a/packages/protocol/scripts/truffle/set_exchange_rate.ts +++ b/packages/protocol/scripts/truffle/set_exchange_rate.ts @@ -133,7 +133,12 @@ module.exports = async (callback: (error?: any) => number) => { elements ) // Report it - await oracles.report(stableToken.address, numerator, denominator, lesserKey, greaterKey) + await oracles.report( + stableToken.address, + numerator.dividedBy(denominator).multipliedBy(await oracles.getDenominator()), + lesserKey, + greaterKey + ) callback() } catch (error) { callback(error) diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index bac7cce8526..c0346cea075 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -4,8 +4,8 @@ import { assertRevert, matchAddress, matchAny, - timeTravel, NULL_ADDRESS, + timeTravel, } from '@celo/protocol/lib/test-utils' import BigNumber from 'bignumber.js' import { SortedOraclesContract, SortedOraclesInstance } from 'types' @@ -21,9 +21,11 @@ contract('SortedOracles', (accounts: string[]) => { const anOracle = accounts[9] const aToken = '0x00000000000000000000000000000000deadbeef' const aReportExpiry: number = 3600 + let fractionDenominator = null beforeEach(async () => { sortedOracles = await SortedOracles.new() + fractionDenominator = await sortedOracles.getDenominator() await sortedOracles.initialize(aReportExpiry) }) @@ -115,7 +117,15 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, 1, 1, NULL_ADDRESS, NULL_ADDRESS, { from: anOracle }) + await sortedOracles.report( + aToken, + new BigNumber(1 / 1).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) }) it('should revert when only 1 report exists', async () => { @@ -128,9 +138,15 @@ contract('SortedOracles', (accounts: string[]) => { for (let i = 7; i > 3; i--) { const anotherOracle = accounts[i] await sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report(aToken, 2, 1, anOracle, NULL_ADDRESS, { - from: anotherOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(2 / 1).multipliedBy(fractionDenominator), + anOracle, + NULL_ADDRESS, + { + from: anotherOracle, + } + ) } }) @@ -170,9 +186,15 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, 10, 1, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(10 / 1).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) }) it('should decrease the number of rates', async () => { @@ -270,32 +292,56 @@ contract('SortedOracles', (accounts: string[]) => { }) it('should increase the number of rates', async () => { - await sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) assert.equal((await sortedOracles.numRates(aToken)).toNumber(), 1) }) it('should set the median rate', async () => { - await sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) const [actualNumerator, actualDenominator] = await sortedOracles.medianRate(aToken) assertEqualBN(actualNumerator, expectedNumerator) assertEqualBN(actualDenominator, expectedDenominator) }) it('should increase the number of timestamps', async () => { - await sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) assertEqualBN(await sortedOracles.numTimestamps(aToken), 1) }) it('should set the median timestamp', async () => { - await sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) const blockTimestamp = (await web3.eth.getBlock('latest')).timestamp assert.equal((await sortedOracles.medianTimestamp(aToken)).toNumber(), blockTimestamp) }) @@ -303,8 +349,7 @@ contract('SortedOracles', (accounts: string[]) => { it('should emit the OracleReported and MedianUpdated events', async () => { const resp = await sortedOracles.report( aToken, - numerator, - denominator, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -335,7 +380,12 @@ contract('SortedOracles', (accounts: string[]) => { it('should revert when called by a non-oracle', async () => { await assertRevert( - sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS) + sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(denominator), + NULL_ADDRESS, + NULL_ADDRESS + ) ) }) @@ -344,18 +394,30 @@ contract('SortedOracles', (accounts: string[]) => { const newExpectedNumerator = expectedNumeratorFromGiven(newNumerator, denominator) beforeEach(async () => { - await sortedOracles.report(aToken, numerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) }) it('should reset the median rate', async () => { const [initialNumerator, initialDenominator] = await sortedOracles.medianRate(aToken) assertEqualBN(initialNumerator, expectedNumerator) assertEqualBN(initialDenominator, expectedDenominator) - await sortedOracles.report(aToken, newNumerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(newNumerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) const [actualNumerator, actualDenominator] = await sortedOracles.medianRate(aToken) assertEqualBN(actualNumerator, newExpectedNumerator) @@ -363,9 +425,15 @@ contract('SortedOracles', (accounts: string[]) => { }) it('should not change the number of total reports', async () => { const initialNumReports = await sortedOracles.numRates(aToken) - await sortedOracles.report(aToken, newNumerator, denominator, NULL_ADDRESS, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anOracle, + } + ) assertEqualBN(initialNumReports, await sortedOracles.numRates(aToken)) }) @@ -387,13 +455,25 @@ contract('SortedOracles', (accounts: string[]) => { beforeEach(async () => { sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report(aToken, anotherOracleNumerator, 1, NULL_ADDRESS, NULL_ADDRESS, { - from: anotherOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(anotherOracleNumerator / 1).multipliedBy(fractionDenominator), + NULL_ADDRESS, + NULL_ADDRESS, + { + from: anotherOracle, + } + ) await timeTravel(5, web3) - await sortedOracles.report(aToken, anOracleNumerator1, 1, anotherOracle, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(anOracleNumerator1 / 1).multipliedBy(fractionDenominator), + anotherOracle, + NULL_ADDRESS, + { + from: anOracle, + } + ) await timeTravel(5, web3) // confirm the setup worked @@ -403,9 +483,15 @@ contract('SortedOracles', (accounts: string[]) => { }) it('updates the list of rates correctly', async () => { - await sortedOracles.report(aToken, anOracleNumerator2, 1, anotherOracle, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(anOracleNumerator2 / 1).multipliedBy(fractionDenominator), + anotherOracle, + NULL_ADDRESS, + { + from: anOracle, + } + ) const resultRates = await sortedOracles.getRates(aToken) assertEqualBN(resultRates['1'][0], anOracleExpectedNumerator2) assertEqualBN(resultRates['1'][1], anotherOracleExpectedNumerator) @@ -413,9 +499,15 @@ contract('SortedOracles', (accounts: string[]) => { it('updates the latest timestamp', async () => { const initialTimestamps = await sortedOracles.getTimestamps(aToken) - await sortedOracles.report(aToken, anOracleNumerator2, 1, anotherOracle, NULL_ADDRESS, { - from: anOracle, - }) + await sortedOracles.report( + aToken, + new BigNumber(anOracleNumerator2 / 1).multipliedBy(fractionDenominator), + anotherOracle, + NULL_ADDRESS, + { + from: anOracle, + } + ) const resultTimestamps = await sortedOracles.getTimestamps(aToken) // the second timestamp, belonging to anotherOracle should be unchanged From b0909786760e510d339c47faf1d47630c50dc856 Mon Sep 17 00:00:00 2001 From: evgenipirianov Date: Thu, 9 Jan 2020 09:27:24 +0100 Subject: [PATCH 02/11] fixes acc. to PR comments --- .../protocol/contracts/common/MultiSig.sol | 1 + .../contracts/governance/EpochRewards.sol | 1 + .../contracts/stability/SortedOracles.sol | 2 +- .../contracts/stability/StableToken.sol | 1 + .../protocol/test/stability/sortedoracles.ts | 46 +++++++++++++------ 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/packages/protocol/contracts/common/MultiSig.sol b/packages/protocol/contracts/common/MultiSig.sol index 3d1c1a05a59..ae0b8774767 100644 --- a/packages/protocol/contracts/common/MultiSig.sol +++ b/packages/protocol/contracts/common/MultiSig.sol @@ -125,6 +125,7 @@ contract MultiSig is Initializable { "owner was null or already given owner status" ); isOwner[_owners[i]] = true; + emit OwnerAddition(_owners[i]); } owners = _owners; } diff --git a/packages/protocol/contracts/governance/EpochRewards.sol b/packages/protocol/contracts/governance/EpochRewards.sol index 91e287ea149..0c447dee17a 100644 --- a/packages/protocol/contracts/governance/EpochRewards.sol +++ b/packages/protocol/contracts/governance/EpochRewards.sol @@ -165,6 +165,7 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry * @notice Sets the target voting yield for validators. * @param _targetVotingYield The value of the target voting yield. * @return True upon success. + * @dev unwrapped Fixidity value expected for _targetVotingYield */ function setTargetVotingYield(uint256 _targetVotingYield) public onlyOwner returns (bool) { targetVotingYieldParams.target = FixidityLib.wrap(_targetVotingYield); diff --git a/packages/protocol/contracts/stability/SortedOracles.sol b/packages/protocol/contracts/stability/SortedOracles.sol index 0835d191e76..4ffa7115069 100644 --- a/packages/protocol/contracts/stability/SortedOracles.sol +++ b/packages/protocol/contracts/stability/SortedOracles.sol @@ -124,7 +124,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { /** * @notice Updates an oracle value and the median. * @param token The address of the token for which the Celo Gold exchange rate is being reported. - * @param value The amount of tokens equal to Celo Gold represented as a FixidityLib fraction + * @param value The amount of tokens equal to Celo Gold represented as a FixidityLib fraction, premultiplied with the denominator * @param lesserKey The element which should be just left of the new oracle value. * @param greaterKey The element which should be just right of the new oracle value. * @dev Note that only one of `lesserKey` or `greaterKey` needs to be correct to reduce friction. diff --git a/packages/protocol/contracts/stability/StableToken.sol b/packages/protocol/contracts/stability/StableToken.sol index 6066720fa81..8f8c13f75ed 100644 --- a/packages/protocol/contracts/stability/StableToken.sol +++ b/packages/protocol/contracts/stability/StableToken.sol @@ -170,6 +170,7 @@ contract StableToken is * @notice Sets initial inflation Parameters. * @param inflationRate new rate. * @param inflationFactorUpdatePeriod how often inflationFactor is updated. + * @dev unwrapped Fixidity value expected for inflationRate */ function setInitialInflationParameters(uint256 inflationRate, uint256 inflationFactorUpdatePeriod) private diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index c0346cea075..124a1318ab6 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -119,7 +119,7 @@ contract('SortedOracles', (accounts: string[]) => { beforeEach(async () => { await sortedOracles.report( aToken, - new BigNumber(1 / 1).multipliedBy(fractionDenominator), + new BigNumber(1).multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -140,7 +140,7 @@ contract('SortedOracles', (accounts: string[]) => { await sortedOracles.addOracle(aToken, anotherOracle) await sortedOracles.report( aToken, - new BigNumber(2 / 1).multipliedBy(fractionDenominator), + new BigNumber(2).multipliedBy(fractionDenominator), anOracle, NULL_ADDRESS, { @@ -188,7 +188,7 @@ contract('SortedOracles', (accounts: string[]) => { beforeEach(async () => { await sortedOracles.report( aToken, - new BigNumber(10 / 1).multipliedBy(fractionDenominator), + new BigNumber(10).multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -294,7 +294,9 @@ contract('SortedOracles', (accounts: string[]) => { it('should increase the number of rates', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -307,7 +309,9 @@ contract('SortedOracles', (accounts: string[]) => { it('should set the median rate', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -322,7 +326,9 @@ contract('SortedOracles', (accounts: string[]) => { it('should increase the number of timestamps', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -335,7 +341,9 @@ contract('SortedOracles', (accounts: string[]) => { it('should set the median timestamp', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -349,7 +357,9 @@ contract('SortedOracles', (accounts: string[]) => { it('should emit the OracleReported and MedianUpdated events', async () => { const resp = await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -396,7 +406,9 @@ contract('SortedOracles', (accounts: string[]) => { beforeEach(async () => { await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(numerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -411,7 +423,9 @@ contract('SortedOracles', (accounts: string[]) => { await sortedOracles.report( aToken, - new BigNumber(newNumerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(newNumerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -427,7 +441,9 @@ contract('SortedOracles', (accounts: string[]) => { const initialNumReports = await sortedOracles.numRates(aToken) await sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(fractionDenominator), + new BigNumber(newNumerator) + .dividedBy(new BigNumber(denominator)) + .multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -457,7 +473,7 @@ contract('SortedOracles', (accounts: string[]) => { sortedOracles.addOracle(aToken, anotherOracle) await sortedOracles.report( aToken, - new BigNumber(anotherOracleNumerator / 1).multipliedBy(fractionDenominator), + new BigNumber(anotherOracleNumerator).multipliedBy(fractionDenominator), NULL_ADDRESS, NULL_ADDRESS, { @@ -467,7 +483,7 @@ contract('SortedOracles', (accounts: string[]) => { await timeTravel(5, web3) await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator1 / 1).multipliedBy(fractionDenominator), + new BigNumber(anOracleNumerator1).multipliedBy(fractionDenominator), anotherOracle, NULL_ADDRESS, { @@ -485,7 +501,7 @@ contract('SortedOracles', (accounts: string[]) => { it('updates the list of rates correctly', async () => { await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator2 / 1).multipliedBy(fractionDenominator), + new BigNumber(anOracleNumerator2).multipliedBy(fractionDenominator), anotherOracle, NULL_ADDRESS, { @@ -501,7 +517,7 @@ contract('SortedOracles', (accounts: string[]) => { const initialTimestamps = await sortedOracles.getTimestamps(aToken) await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator2 / 1).multipliedBy(fractionDenominator), + new BigNumber(anOracleNumerator2).multipliedBy(fractionDenominator), anotherOracle, NULL_ADDRESS, { From 62cca9be0a8d20b1e66450895cd7ad01676c8c8e Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Tue, 14 Jan 2020 15:16:05 +0200 Subject: [PATCH 03/11] MultiSig -> simplify initialization with addOwner and changeRequirement --- .../protocol/contracts/common/MultiSig.sol | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/packages/protocol/contracts/common/MultiSig.sol b/packages/protocol/contracts/common/MultiSig.sol index 3d1c1a05a59..3039fcf6ef8 100644 --- a/packages/protocol/contracts/common/MultiSig.sol +++ b/packages/protocol/contracts/common/MultiSig.sol @@ -110,47 +110,29 @@ contract MultiSig is Initializable { initializer validRequirement(_owners.length, _required) { - setInitialOwners(_owners); - setInitialRequired(_required); - } - - /** - * @notice Sets the initial owners of the wallet. - * @param _owners List of initial owners. - */ - function setInitialOwners(address[] memory _owners) private { for (uint256 i = 0; i < _owners.length; i++) { - require( - !isOwner[_owners[i]] && _owners[i] != address(0), - "owner was null or already given owner status" - ); - isOwner[_owners[i]] = true; + _addOwner(_owners[i]); } - owners = _owners; + changeRequirement(_required); } - /** - * @notice Sets the required number of tx confirmations. - * @param _required The number of required tx confirmations. - */ - function setInitialRequired(uint256 _required) private { - require(_required > 0, "Required confirmations must be greater than zero"); - required = _required; - emit RequirementSet(_required); + /// @dev Allows to add a new owner. Transaction has to be sent by wallet. + /// @param owner Address of new owner. + function addOwner(address owner) external onlyWallet { + _addOwner(owner); } - /// @dev Allows to add a new owner. Transaction has to be sent by wallet. + /// @dev adding new owner /// @param owner Address of new owner. - function addOwner(address owner) - external - onlyWallet + function _addOwner(address _owner) + private ownerDoesNotExist(owner) notNull(owner) validRequirement(owners.length + 1, required) { - isOwner[owner] = true; - owners.push(owner); - emit OwnerAddition(owner); + isOwner[_owner] = true; + owners.push(_owner); + emit OwnerAddition(_owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. From 735755f8a3deaec21150220e3f338ba48c45ff2f Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Tue, 14 Jan 2020 18:16:15 +0200 Subject: [PATCH 04/11] MultiSig -> simplify initialization with addOwner and changeRequirement naming fixes, EpochRewards -> few setters switched to privet, Governance -> setLastDequeue() switched to private, StableToken -> setters for name, symbol, and decimals removed --- .../protocol/contracts/common/MultiSig.sol | 25 +++++---- .../contracts/governance/EpochRewards.sol | 4 +- .../contracts/governance/Governance.sol | 2 +- .../contracts/stability/StableToken.sol | 51 ++----------------- 4 files changed, 20 insertions(+), 62 deletions(-) diff --git a/packages/protocol/contracts/common/MultiSig.sol b/packages/protocol/contracts/common/MultiSig.sol index 3039fcf6ef8..6d19d58db47 100644 --- a/packages/protocol/contracts/common/MultiSig.sol +++ b/packages/protocol/contracts/common/MultiSig.sol @@ -113,26 +113,25 @@ contract MultiSig is Initializable { for (uint256 i = 0; i < _owners.length; i++) { _addOwner(_owners[i]); } - changeRequirement(_required); + _changeRequirement(_required); } /// @dev Allows to add a new owner. Transaction has to be sent by wallet. /// @param owner Address of new owner. - function addOwner(address owner) external onlyWallet { + function addOwner(address owner) + external + onlyWallet + validRequirement(owners.length + 1, required) + { _addOwner(owner); } /// @dev adding new owner /// @param owner Address of new owner. - function _addOwner(address _owner) - private - ownerDoesNotExist(owner) - notNull(owner) - validRequirement(owners.length + 1, required) - { - isOwner[_owner] = true; - owners.push(_owner); - emit OwnerAddition(_owner); + function _addOwner(address owner) private ownerDoesNotExist(owner) notNull(owner) { + isOwner[owner] = true; + owners.push(owner); + emit OwnerAddition(owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. @@ -177,6 +176,10 @@ contract MultiSig is Initializable { onlyWallet validRequirement(owners.length, _required) { + _changeRequirement(_required); + } + + function _changeRequirement(uint256 _required) private { required = _required; emit RequirementChange(_required); } diff --git a/packages/protocol/contracts/governance/EpochRewards.sol b/packages/protocol/contracts/governance/EpochRewards.sol index 91e287ea149..f8151bc38ad 100644 --- a/packages/protocol/contracts/governance/EpochRewards.sol +++ b/packages/protocol/contracts/governance/EpochRewards.sol @@ -166,7 +166,7 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry * @param _targetVotingYield The value of the target voting yield. * @return True upon success. */ - function setTargetVotingYield(uint256 _targetVotingYield) public onlyOwner returns (bool) { + function setTargetVotingYield(uint256 _targetVotingYield) private returns (bool) { targetVotingYieldParams.target = FixidityLib.wrap(_targetVotingYield); emit TargetVotingYield(_targetVotingYield); return true; @@ -177,7 +177,7 @@ contract EpochRewards is Ownable, Initializable, UsingPrecompiles, UsingRegistry * @param _startTime The time in unix. * @return True upon success. */ - function setStartTime(uint256 _startTime) public onlyOwner returns (bool) { + function setStartTime(uint256 _startTime) private returns (bool) { require(_startTime >= block.timestamp, "The start time must not be in the past"); startTime = _startTime; emit StartTimeSet(_startTime); diff --git a/packages/protocol/contracts/governance/Governance.sol b/packages/protocol/contracts/governance/Governance.sol index 940d508bf24..a4aa6b90a77 100644 --- a/packages/protocol/contracts/governance/Governance.sol +++ b/packages/protocol/contracts/governance/Governance.sol @@ -238,7 +238,7 @@ contract Governance is * @notice Updates the last dequeue timestamp. * @param _lastDequeue The last dequeue timestamp. */ - function setLastDequeue(uint256 _lastDequeue) public onlyOwner { + function setLastDequeue(uint256 _lastDequeue) private { require(_lastDequeue >= block.timestamp, "last dequeuing time must not be in the past"); // solhint-disable-next-line not-rely-on-time lastDequeue = _lastDequeue; diff --git a/packages/protocol/contracts/stability/StableToken.sol b/packages/protocol/contracts/stability/StableToken.sol index 6066720fa81..9340e5edf67 100644 --- a/packages/protocol/contracts/stability/StableToken.sol +++ b/packages/protocol/contracts/stability/StableToken.sol @@ -35,14 +35,6 @@ contract StableToken is event TransferComment(string comment); - event TotalSupplySet(address sender, uint256 totalSupply); - - event TokenNameSet(address sender, string name); - - event TokenSymbolSet(address sender, string symbol); - - event TokenDecimalslSet(address sender, uint8 decimals); - string internal name_; string internal symbol_; uint8 internal decimals_; @@ -117,10 +109,9 @@ contract StableToken is require(inflationRate != 0, "Must provide a non-zero inflation rate."); _transferOwnership(msg.sender); - setTotalSupply(0); - setName(_name); - setSymbol(_symbol); - setDecimals(_decimals); + name_ = _name; + symbol_ = _symbol; + decimals_ = _decimals; setInitialInflationParameters(inflationRate, inflationFactorUpdatePeriod); require(initialBalanceAddresses.length == initialBalanceValues.length); @@ -130,42 +121,6 @@ contract StableToken is setRegistry(registryAddress); } - /** - * @notice Setter for the total supply. - * @param totalSupply The total supply to set. - */ - function setTotalSupply(uint256 totalSupply) public { - totalSupply_ = totalSupply; - emit TotalSupplySet(msg.sender, totalSupply); - } - - /** - * @notice Setter for the name of the token. - * @param name The name to set. - */ - function setName(string memory name) public { - name_ = name; - emit TokenNameSet(msg.sender, name); - } - - /** - * @notice Setter for the symbol of the token. - * @param symbol The symbol to set. - */ - function setSymbol(string memory symbol) public { - symbol_ = symbol; - emit TokenSymbolSet(msg.sender, symbol); - } - - /** - * @notice Setter for the decimals of the token. - * @param decimals The decimals to set. - */ - function setDecimals(uint8 decimals) public { - decimals_ = decimals; - emit TokenDecimalslSet(msg.sender, decimals); - } - /** * @notice Sets initial inflation Parameters. * @param inflationRate new rate. From b5f49355571871e5ebc9f15e487bc66949e8c993 Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Fri, 17 Jan 2020 10:59:35 +0200 Subject: [PATCH 05/11] DENOMINATOR / getDenominator removed, numerator, denominator replaced with medianValue, unit test update --- .../contracts/stability/SortedOracles.sol | 31 +--- .../protocol/migrations/08_stabletoken.ts | 4 +- .../scripts/truffle/set_exchange_rate.ts | 2 +- .../protocol/test/stability/sortedoracles.ts | 154 +++++++----------- 4 files changed, 68 insertions(+), 123 deletions(-) diff --git a/packages/protocol/contracts/stability/SortedOracles.sol b/packages/protocol/contracts/stability/SortedOracles.sol index 4ffa7115069..905519ad8d0 100644 --- a/packages/protocol/contracts/stability/SortedOracles.sol +++ b/packages/protocol/contracts/stability/SortedOracles.sol @@ -17,9 +17,6 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { using AddressSortedLinkedListWithMedian for SortedLinkedListWithMedian.List; using FixidityLib for FixidityLib.Fraction; - // All oracle rates are assumed to have a denominator of 2 ^ 64. - uint256 private constant DENOMINATOR = 0x10000000000000000; - // Maps a token address to a sorted list of report values. mapping(address => SortedLinkedListWithMedian.List) private rates; // Maps a token address to a sorted list of report timestamps. @@ -33,17 +30,11 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { event OracleRemoved(address indexed token, address indexed oracleAddress); - event OracleReported( - address token, - address oracle, - uint256 timestamp, - uint256 numerator, - uint256 denominator - ); + event OracleReported(address token, address oracle, uint256 timestamp, uint256 value); event OracleReportRemoved(address indexed token, address indexed oracle); - event MedianUpdated(address token, uint256 numerator, uint256 denominator); + event MedianUpdated(address token, uint256 newMedian); event ReportExpirySet(uint256 reportExpiry); @@ -124,7 +115,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { /** * @notice Updates an oracle value and the median. * @param token The address of the token for which the Celo Gold exchange rate is being reported. - * @param value The amount of tokens equal to Celo Gold represented as a FixidityLib fraction, premultiplied with the denominator + * @param value The amount of tokens equal to Celo Gold represented as a FixidityLib fraction * @param lesserKey The element which should be just left of the new oracle value. * @param greaterKey The element which should be just right of the new oracle value. * @dev Note that only one of `lesserKey` or `greaterKey` needs to be correct to reduce friction. @@ -159,21 +150,13 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { timestamps[token].getHead(), address(0) ); - emit OracleReported(token, msg.sender, now, fractionalValueReported.unwrap(), getDenominator()); + emit OracleReported(token, msg.sender, now, fractionalValueReported.unwrap()); uint256 newMedian = rates[token].getMedianValue(); if (newMedian != originalMedian) { - emit MedianUpdated(token, newMedian, getDenominator()); + emit MedianUpdated(token, newMedian); } } - /** - * @notice Returns the common fixidity denominator. - * @return The common fixidity denominator. - */ - function getDenominator() public view returns (uint256) { - return DENOMINATOR; - } - /** * @notice Returns the number of rates. * @param token The address of the token for which the Celo Gold exchange rate is being reported. @@ -189,7 +172,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { * @return The median exchange rate for `token`. */ function medianRate(address token) external view returns (uint256, uint256) { - return (rates[token].getMedianValue(), numRates(token) == 0 ? 0 : getDenominator()); + return (rates[token].getMedianValue(), numRates(token)); } /** @@ -258,7 +241,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { emit OracleReportRemoved(token, oracle); uint256 newMedian = rates[token].getMedianValue(); if (newMedian != originalMedian) { - emit MedianUpdated(token, newMedian, numRates(token) == 0 ? 0 : getDenominator()); + emit MedianUpdated(token, newMedian); } } } diff --git a/packages/protocol/migrations/08_stabletoken.ts b/packages/protocol/migrations/08_stabletoken.ts index 895f565845a..7828eff6f8d 100644 --- a/packages/protocol/migrations/08_stabletoken.ts +++ b/packages/protocol/migrations/08_stabletoken.ts @@ -59,9 +59,7 @@ module.exports = deploymentForCoreContract( await sortedOracles.report( stableToken.address, - new BigNumber(config.stableToken.goldPrice / 1).multipliedBy( - await sortedOracles.getDenominator() - ), + new BigNumber(config.stableToken.goldPrice / 1), NULL_ADDRESS, NULL_ADDRESS ) diff --git a/packages/protocol/scripts/truffle/set_exchange_rate.ts b/packages/protocol/scripts/truffle/set_exchange_rate.ts index 49c1667e66a..d16761ae991 100644 --- a/packages/protocol/scripts/truffle/set_exchange_rate.ts +++ b/packages/protocol/scripts/truffle/set_exchange_rate.ts @@ -135,7 +135,7 @@ module.exports = async (callback: (error?: any) => number) => { // Report it await oracles.report( stableToken.address, - numerator.dividedBy(denominator).multipliedBy(await oracles.getDenominator()), + numerator.dividedBy(denominator), lesserKey, greaterKey ) diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index 124a1318ab6..6494ad4d603 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -9,6 +9,8 @@ import { } from '@celo/protocol/lib/test-utils' import BigNumber from 'bignumber.js' import { SortedOraclesContract, SortedOraclesInstance } from 'types' +import { toFixed } from '@celo/utils/lib/fixidity' +import { assert } from 'chai' const SortedOracles: SortedOraclesContract = artifacts.require('SortedOracles') @@ -21,11 +23,9 @@ contract('SortedOracles', (accounts: string[]) => { const anOracle = accounts[9] const aToken = '0x00000000000000000000000000000000deadbeef' const aReportExpiry: number = 3600 - let fractionDenominator = null beforeEach(async () => { sortedOracles = await SortedOracles.new() - fractionDenominator = await sortedOracles.getDenominator() await sortedOracles.initialize(aReportExpiry) }) @@ -117,15 +117,9 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report( - aToken, - new BigNumber(1).multipliedBy(fractionDenominator), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, new BigNumber(1), NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) }) it('should revert when only 1 report exists', async () => { @@ -138,15 +132,9 @@ contract('SortedOracles', (accounts: string[]) => { for (let i = 7; i > 3; i--) { const anotherOracle = accounts[i] await sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report( - aToken, - new BigNumber(2).multipliedBy(fractionDenominator), - anOracle, - NULL_ADDRESS, - { - from: anotherOracle, - } - ) + await sortedOracles.report(aToken, new BigNumber(2), anOracle, NULL_ADDRESS, { + from: anotherOracle, + }) } }) @@ -186,15 +174,9 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report( - aToken, - new BigNumber(10).multipliedBy(fractionDenominator), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, new BigNumber(10), NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) }) it('should decrease the number of rates', async () => { @@ -204,8 +186,8 @@ contract('SortedOracles', (accounts: string[]) => { it('should reset the median rate', async () => { await sortedOracles.removeOracle(aToken, anOracle, 0) - const [actualNumerator] = await sortedOracles.medianRate(aToken) - assert.equal(actualNumerator.toNumber(), 0) + const [actualMedianRate] = await sortedOracles.medianRate(aToken) + assert.equal(actualMedianRate.toNumber(), 0) }) it('should decrease the number of timestamps', async () => { @@ -233,8 +215,7 @@ contract('SortedOracles', (accounts: string[]) => { event: 'MedianUpdated', args: { token: matchAddress(aToken), - numerator: new BigNumber(0), - denominator: new BigNumber(0), + newMedian: new BigNumber(0), }, }) @@ -275,18 +256,16 @@ contract('SortedOracles', (accounts: string[]) => { }) describe('#report', () => { - function expectedNumeratorFromGiven( + function expectedMedianRateFromGiven( givenNumerator: number | BigNumber, givenDenominator: number | BigNumber ): BigNumber { - return expectedDenominator.times(givenNumerator).div(givenDenominator) + return toFixed(new BigNumber(givenNumerator).dividedBy(new BigNumber(givenDenominator))) } - const numerator = 10 + const numerator = 5 const denominator = 1 - const expectedDenominator = new BigNumber(2).pow(64) - const expectedNumerator = expectedNumeratorFromGiven(numerator, denominator) - + const expectedMedianRate = expectedMedianRateFromGiven(numerator, denominator) beforeEach(async () => { await sortedOracles.addOracle(aToken, anOracle) }) @@ -294,9 +273,7 @@ contract('SortedOracles', (accounts: string[]) => { it('should increase the number of rates', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -309,26 +286,22 @@ contract('SortedOracles', (accounts: string[]) => { it('should set the median rate', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { from: anOracle, } ) - const [actualNumerator, actualDenominator] = await sortedOracles.medianRate(aToken) - assertEqualBN(actualNumerator, expectedNumerator) - assertEqualBN(actualDenominator, expectedDenominator) + const [actualMedianRate, numberOfRates] = await sortedOracles.medianRate(aToken) + assertEqualBN(actualMedianRate, expectedMedianRate) + assertEqualBN(numberOfRates.toNumber(), 1) }) it('should increase the number of timestamps', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -341,9 +314,7 @@ contract('SortedOracles', (accounts: string[]) => { it('should set the median timestamp', async () => { await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -357,9 +328,7 @@ contract('SortedOracles', (accounts: string[]) => { it('should emit the OracleReported and MedianUpdated events', async () => { const resp = await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -373,8 +342,7 @@ contract('SortedOracles', (accounts: string[]) => { token: matchAddress(aToken), oracle: matchAddress(anOracle), timestamp: matchAny, - numerator: expectedNumerator, - denominator: expectedDenominator, + value: expectedMedianRate, }, }) @@ -382,8 +350,7 @@ contract('SortedOracles', (accounts: string[]) => { event: 'MedianUpdated', args: { token: matchAddress(aToken), - numerator: expectedNumerator, - denominator: expectedDenominator, + newMedian: expectedMedianRate, }, }) }) @@ -392,7 +359,7 @@ contract('SortedOracles', (accounts: string[]) => { await assertRevert( sortedOracles.report( aToken, - new BigNumber(numerator / denominator).multipliedBy(denominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS ) @@ -400,15 +367,13 @@ contract('SortedOracles', (accounts: string[]) => { }) describe('when there exists exactly one other report, made by this oracle', () => { - const newNumerator = 12 - const newExpectedNumerator = expectedNumeratorFromGiven(newNumerator, denominator) + const newMedianRate = 12 + const newExpectedMedianRate = expectedMedianRateFromGiven(newMedianRate, denominator) beforeEach(async () => { await sortedOracles.report( aToken, - new BigNumber(numerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -417,15 +382,11 @@ contract('SortedOracles', (accounts: string[]) => { ) }) it('should reset the median rate', async () => { - const [initialNumerator, initialDenominator] = await sortedOracles.medianRate(aToken) - assertEqualBN(initialNumerator, expectedNumerator) - assertEqualBN(initialDenominator, expectedDenominator) - + const [initialMedianRate] = await sortedOracles.medianRate(aToken) + assertEqualBN(initialMedianRate, newExpectedMedianRate) await sortedOracles.report( aToken, - new BigNumber(newNumerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -433,17 +394,14 @@ contract('SortedOracles', (accounts: string[]) => { } ) - const [actualNumerator, actualDenominator] = await sortedOracles.medianRate(aToken) - assertEqualBN(actualNumerator, newExpectedNumerator) - assertEqualBN(actualDenominator, expectedDenominator) + const [actualMedianRate] = await sortedOracles.medianRate(aToken) + assertEqualBN(actualMedianRate, newExpectedMedianRate) }) it('should not change the number of total reports', async () => { const initialNumReports = await sortedOracles.numRates(aToken) await sortedOracles.report( aToken, - new BigNumber(newNumerator) - .dividedBy(new BigNumber(denominator)) - .multipliedBy(fractionDenominator), + toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -457,15 +415,21 @@ contract('SortedOracles', (accounts: string[]) => { describe('when there are multiple reports, the most recent one done by this oracle', () => { const anotherOracle = accounts[6] - const anOracleNumerator1 = 2 - const anOracleNumerator2 = 3 - const anotherOracleNumerator = 1 + const anOracleMedianRate1 = 2 + const anOracleMedianRate2 = 3 + const anotherOracleMedianRate = 1 - const anOracleExpectedNumerator1 = expectedNumeratorFromGiven(anOracleNumerator1, denominator) - const anOracleExpectedNumerator2 = expectedNumeratorFromGiven(anOracleNumerator2, denominator) + const anOracleExpectedMedianRate1 = expectedMedianRateFromGiven( + anOracleMedianRate1, + denominator + ) + const anOracleExpectedMedianRate2 = expectedMedianRateFromGiven( + anOracleMedianRate2, + denominator + ) - const anotherOracleExpectedNumerator = expectedNumeratorFromGiven( - anotherOracleNumerator, + const anotherOracleExpectedMedianRate = expectedMedianRateFromGiven( + anotherOracleMedianRate, denominator ) @@ -473,7 +437,7 @@ contract('SortedOracles', (accounts: string[]) => { sortedOracles.addOracle(aToken, anotherOracle) await sortedOracles.report( aToken, - new BigNumber(anotherOracleNumerator).multipliedBy(fractionDenominator), + toFixed(new BigNumber(anotherOracleMedianRate).dividedBy(new BigNumber(denominator))), NULL_ADDRESS, NULL_ADDRESS, { @@ -483,7 +447,7 @@ contract('SortedOracles', (accounts: string[]) => { await timeTravel(5, web3) await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator1).multipliedBy(fractionDenominator), + toFixed(new BigNumber(anOracleMedianRate1).dividedBy(new BigNumber(denominator))), anotherOracle, NULL_ADDRESS, { @@ -494,14 +458,14 @@ contract('SortedOracles', (accounts: string[]) => { // confirm the setup worked const initialRates = await sortedOracles.getRates(aToken) - assertEqualBN(initialRates['1'][0], anOracleExpectedNumerator1) - assertEqualBN(initialRates['1'][1], anotherOracleExpectedNumerator) + assertEqualBN(initialRates['1'][0], anOracleExpectedMedianRate1) + assertEqualBN(initialRates['1'][1], anotherOracleExpectedMedianRate) }) it('updates the list of rates correctly', async () => { await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator2).multipliedBy(fractionDenominator), + toFixed(new BigNumber(anOracleMedianRate2).dividedBy(new BigNumber(denominator))), anotherOracle, NULL_ADDRESS, { @@ -509,15 +473,15 @@ contract('SortedOracles', (accounts: string[]) => { } ) const resultRates = await sortedOracles.getRates(aToken) - assertEqualBN(resultRates['1'][0], anOracleExpectedNumerator2) - assertEqualBN(resultRates['1'][1], anotherOracleExpectedNumerator) + assertEqualBN(resultRates['1'][0], anOracleExpectedMedianRate2) + assertEqualBN(resultRates['1'][1], anotherOracleExpectedMedianRate) }) it('updates the latest timestamp', async () => { const initialTimestamps = await sortedOracles.getTimestamps(aToken) await sortedOracles.report( aToken, - new BigNumber(anOracleNumerator2).multipliedBy(fractionDenominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), anotherOracle, NULL_ADDRESS, { From 8c6c65aed27fd162a6ad057b16130d3970d1b99b Mon Sep 17 00:00:00 2001 From: evgenipirianov Date: Fri, 17 Jan 2020 16:33:41 +0100 Subject: [PATCH 06/11] fixed numerator/denominatpr in soredoracles kit --- packages/contractkit/src/wrappers/SortedOracles.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/contractkit/src/wrappers/SortedOracles.ts b/packages/contractkit/src/wrappers/SortedOracles.ts index fe8d39abd2c..4c662348a0a 100644 --- a/packages/contractkit/src/wrappers/SortedOracles.ts +++ b/packages/contractkit/src/wrappers/SortedOracles.ts @@ -1,4 +1,5 @@ import { eqAddress } from '@celo/utils/lib/address' +import { toFixed } from '@celo/utils/src/fixidity' import BigNumber from 'bignumber.js' import { Address, CeloContract, CeloToken, NULL_ADDRESS } from '../base' import { SortedOracles } from '../generated/types/SortedOracles' @@ -98,9 +99,9 @@ export class SortedOraclesWrapper extends BaseWrapper { oracleAddress ) - const reportedValue = toBigNumber(numerator.toString()) - .dividedBy(toBigNumber(denominator.toString())) - .multipliedBy(await this.getInternalDenominator()) + const reportedValue = toFixed( + toBigNumber(numerator.toString()).dividedBy(toBigNumber(denominator.toString())) + ).toString() return toTransactionObject( this.kit, From 978ae1eb12df336e228fb052854385aa8b675d24 Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Tue, 21 Jan 2020 15:34:06 +0200 Subject: [PATCH 07/11] factor can be update after 18 months --- .../contracts/stability/StableToken.sol | 4 +- .../protocol/test/stability/stabletoken.ts | 50 +++++++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/packages/protocol/contracts/stability/StableToken.sol b/packages/protocol/contracts/stability/StableToken.sol index fe0fa6cef60..4183a11fb40 100644 --- a/packages/protocol/contracts/stability/StableToken.sol +++ b/packages/protocol/contracts/stability/StableToken.sol @@ -61,6 +61,8 @@ contract StableToken is InflationState inflationState; + uint256 constant eighteenMonthsInSeconds = 47340000; + /** * Only VM would be able to set the caller address to 0x0 unless someone * really has the private key for 0x0 @@ -142,7 +144,7 @@ contract StableToken is ); inflationState.factor = FixidityLib.fixed1(); - inflationState.factorLastUpdated = now; + inflationState.factorLastUpdated = now.add(eighteenMonthsInSeconds); emit InflationFactorUpdated(inflationState.factor.unwrap(), inflationState.factorLastUpdated); } diff --git a/packages/protocol/test/stability/stabletoken.ts b/packages/protocol/test/stability/stabletoken.ts index 87557e3dbec..863f39c4489 100644 --- a/packages/protocol/test/stability/stabletoken.ts +++ b/packages/protocol/test/stability/stabletoken.ts @@ -13,6 +13,7 @@ import { RegistryInstance, StableTokenInstance } from 'types' const Registry: Truffle.Contract = artifacts.require('Registry') const StableToken: Truffle.Contract = artifacts.require('StableToken') +const EighteenMonthsInSeconds = 47340000 // @ts-ignore // TODO(mcortesi): Use BN.js @@ -78,7 +79,7 @@ contract('StableToken', (accounts: string[]) => { assert.isTrue(rate.eq(fixed1)) assert.isTrue(factor.eq(fixed1)) assert.equal(updatePeriod.toNumber(), SECONDS_IN_A_WEEK) - assert.equal(factorLastUpdated.toNumber(), initializationTime) + assert.equal(factorLastUpdated.toNumber(), initializationTime + EighteenMonthsInSeconds) }) it('should not be callable again', async () => { @@ -178,13 +179,17 @@ contract('StableToken', (accounts: string[]) => { beforeEach(async () => { await stableToken.setInflationParameters(inflationRate, SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) }) it('should update factor', async () => { await stableToken.transferWithComment(receiver, 5, comment) const [, factor, updatePeriod, lastUpdated] = await stableToken.getInflationParameters() assert.isTrue(factor.eq(inflationRate)) - assert.equal(lastUpdated.toNumber(), initializationTime + updatePeriod.toNumber()) + assert.equal( + lastUpdated.toNumber(), + initializationTime + EighteenMonthsInSeconds + updatePeriod.toNumber() + ) }) it('should emit InflationFactorUpdated event', async () => { @@ -193,7 +198,7 @@ contract('StableToken', (accounts: string[]) => { event: 'InflationFactorUpdated', args: { factor: inflationRate, - lastUpdated: initializationTime + SECONDS_IN_A_WEEK, + lastUpdated: initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds, }, }) }) @@ -208,7 +213,7 @@ contract('StableToken', (accounts: string[]) => { const [rate, , updatePeriod, lastUpdated] = await stableToken.getInflationParameters() assert.isTrue(rate.eq(inflationRate)) assert.equal(updatePeriod.toNumber(), newUpdatePeriod) - assert.equal(lastUpdated.toNumber(), initializationTime) + assert.equal(lastUpdated.toNumber(), initializationTime + EighteenMonthsInSeconds) }) it('should emit an InflationParametersUpdated event', async () => { @@ -231,6 +236,7 @@ contract('StableToken', (accounts: string[]) => { const newRate = toFixed(1) await stableToken.setInflationParameters(initialRate, SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) const res = await stableToken.setInflationParameters(newRate, SECONDS_IN_A_WEEK) const [rate, factor, , lastUpdated] = await stableToken.getInflationParameters() assertLogMatches2(res.logs[0], { @@ -273,6 +279,7 @@ contract('StableToken', (accounts: string[]) => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) }) it('should return depreciated balance value', async () => { @@ -286,6 +293,7 @@ contract('StableToken', (accounts: string[]) => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) }) it('value 995 should correspond to roughly 1000 units after .005 depreciation', async () => { @@ -306,6 +314,7 @@ contract('StableToken', (accounts: string[]) => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) }) it('1000 in units should be 995 in value after .005 depreciation', async () => { @@ -384,6 +393,7 @@ contract('StableToken', (accounts: string[]) => { await stableToken.mint(sender, amount.times(2)) await stableToken.setInflationParameters(inflationRate, SECONDS_IN_A_WEEK) await timeTravel(SECONDS_IN_A_WEEK, web3) + await timeTravel(EighteenMonthsInSeconds, web3) }) async function assertInflationUpdatedEvent(log, requestBlockTime, inflationPeriods = 1) { @@ -395,27 +405,42 @@ contract('StableToken', (accounts: string[]) => { it('setInflationParameters', async () => { const res = await stableToken.setInflationParameters(fixed1, SECONDS_IN_A_WEEK) - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) it('approve', async () => { const res = await stableToken.approve(receiver, amount) - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) it('mint', async () => { const res = await stableToken.mint(sender, amountToMint) - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) it('transferWithComment', async () => { const res = await stableToken.transferWithComment(receiver, amount, 'hi') - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) it('burn', async () => { const res = await stableToken.mint(sender, amount) - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) it('transferFrom', async () => { @@ -424,14 +449,17 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.transferFrom(sender, receiver, amount, { from: receiver }) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK * 2, + initializationTime + SECONDS_IN_A_WEEK * 2 + EighteenMonthsInSeconds, 2 ) }) it('transfer', async () => { const res = await stableToken.transfer(receiver, 1) - await assertInflationUpdatedEvent(res.logs[0], initializationTime + SECONDS_IN_A_WEEK) + await assertInflationUpdatedEvent( + res.logs[0], + initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + ) }) }) }) From 22071bd67bb67041ca125cfdda2290c106b95a3a Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Wed, 22 Jan 2020 13:50:56 +0200 Subject: [PATCH 08/11] MultiSig -> removed unused event, 08_stabletoken.ts and set_exchange_rate.ts the median rate is multiplied by 10^24 (toFixed), sortedoracles.ts (unittests) numerator / denomerator replaced with rate --- .../protocol/contracts/common/MultiSig.sol | 1 - .../protocol/migrations/08_stabletoken.ts | 2 +- .../scripts/truffle/set_exchange_rate.ts | 3 +- .../protocol/test/stability/sortedoracles.ts | 164 +++++------------- .../protocol/test/stability/stabletoken.ts | 36 ++-- 5 files changed, 66 insertions(+), 140 deletions(-) diff --git a/packages/protocol/contracts/common/MultiSig.sol b/packages/protocol/contracts/common/MultiSig.sol index 6d19d58db47..c392a3ab015 100644 --- a/packages/protocol/contracts/common/MultiSig.sol +++ b/packages/protocol/contracts/common/MultiSig.sol @@ -20,7 +20,6 @@ contract MultiSig is Initializable { event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event RequirementChange(uint256 required); - event RequirementSet(uint256 required); /* * Constants diff --git a/packages/protocol/migrations/08_stabletoken.ts b/packages/protocol/migrations/08_stabletoken.ts index 7828eff6f8d..9bbac7da1e3 100644 --- a/packages/protocol/migrations/08_stabletoken.ts +++ b/packages/protocol/migrations/08_stabletoken.ts @@ -59,7 +59,7 @@ module.exports = deploymentForCoreContract( await sortedOracles.report( stableToken.address, - new BigNumber(config.stableToken.goldPrice / 1), + toFixed(new BigNumber(config.stableToken.goldPrice).dividedBy(new BigNumber(1))), NULL_ADDRESS, NULL_ADDRESS ) diff --git a/packages/protocol/scripts/truffle/set_exchange_rate.ts b/packages/protocol/scripts/truffle/set_exchange_rate.ts index d16761ae991..e91bf8694cf 100644 --- a/packages/protocol/scripts/truffle/set_exchange_rate.ts +++ b/packages/protocol/scripts/truffle/set_exchange_rate.ts @@ -2,6 +2,7 @@ import { getDeployedProxiedContract } from '@celo/protocol/lib/web3-utils' import { BigNumber } from 'bignumber.js' import { SortedOraclesInstance, StableTokenInstance } from 'types' +import { toFixed } from '@celo/utils/lib/fixidity' const fs = require('fs') const parse = require('csv-parser') @@ -135,7 +136,7 @@ module.exports = async (callback: (error?: any) => number) => { // Report it await oracles.report( stableToken.address, - numerator.dividedBy(denominator), + toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), lesserKey, greaterKey ) diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index 6494ad4d603..c8fac801137 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -117,7 +117,7 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, new BigNumber(1), NULL_ADDRESS, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(new BigNumber(1)), NULL_ADDRESS, NULL_ADDRESS, { from: anOracle, }) }) @@ -132,7 +132,7 @@ contract('SortedOracles', (accounts: string[]) => { for (let i = 7; i > 3; i--) { const anotherOracle = accounts[i] await sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report(aToken, new BigNumber(2), anOracle, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(new BigNumber(2)), anOracle, NULL_ADDRESS, { from: anotherOracle, }) } @@ -174,7 +174,7 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, new BigNumber(10), NULL_ADDRESS, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(new BigNumber(10)), NULL_ADDRESS, NULL_ADDRESS, { from: anOracle, }) }) @@ -256,85 +256,46 @@ contract('SortedOracles', (accounts: string[]) => { }) describe('#report', () => { - function expectedMedianRateFromGiven( - givenNumerator: number | BigNumber, - givenDenominator: number | BigNumber - ): BigNumber { - return toFixed(new BigNumber(givenNumerator).dividedBy(new BigNumber(givenDenominator))) - } - - const numerator = 5 - const denominator = 1 - const expectedMedianRate = expectedMedianRateFromGiven(numerator, denominator) + const givenMedianRate = toFixed(new BigNumber(5).dividedBy(new BigNumber(1))) beforeEach(async () => { await sortedOracles.addOracle(aToken, anOracle) }) it('should increase the number of rates', async () => { - await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) assert.equal((await sortedOracles.numRates(aToken)).toNumber(), 1) }) it('should set the median rate', async () => { - await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) const [actualMedianRate, numberOfRates] = await sortedOracles.medianRate(aToken) - assertEqualBN(actualMedianRate, expectedMedianRate) + assertEqualBN(actualMedianRate, givenMedianRate) assertEqualBN(numberOfRates.toNumber(), 1) }) it('should increase the number of timestamps', async () => { - await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) assertEqualBN(await sortedOracles.numTimestamps(aToken), 1) }) it('should set the median timestamp', async () => { - await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) const blockTimestamp = (await web3.eth.getBlock('latest')).timestamp assert.equal((await sortedOracles.medianTimestamp(aToken)).toNumber(), blockTimestamp) }) it('should emit the OracleReported and MedianUpdated events', async () => { - const resp = await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + const resp = await sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) assert.equal(resp.logs.length, 2) assertLogMatches2(resp.logs[0], { event: 'OracleReported', @@ -342,7 +303,7 @@ contract('SortedOracles', (accounts: string[]) => { token: matchAddress(aToken), oracle: matchAddress(anOracle), timestamp: matchAny, - value: expectedMedianRate, + value: givenMedianRate, }, }) @@ -350,64 +311,38 @@ contract('SortedOracles', (accounts: string[]) => { event: 'MedianUpdated', args: { token: matchAddress(aToken), - newMedian: expectedMedianRate, + newMedian: givenMedianRate, }, }) }) it('should revert when called by a non-oracle', async () => { - await assertRevert( - sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS - ) - ) + await assertRevert(sortedOracles.report(aToken, givenMedianRate, NULL_ADDRESS, NULL_ADDRESS)) }) describe('when there exists exactly one other report, made by this oracle', () => { - const newMedianRate = 12 - const newExpectedMedianRate = expectedMedianRateFromGiven(newMedianRate, denominator) + const newExpectedMedianRate = toFixed(new BigNumber(12).dividedBy(new BigNumber(1))) beforeEach(async () => { - await sortedOracles.report( - aToken, - toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, newExpectedMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) }) it('should reset the median rate', async () => { const [initialMedianRate] = await sortedOracles.medianRate(aToken) assertEqualBN(initialMedianRate, newExpectedMedianRate) - await sortedOracles.report( - aToken, - toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, newExpectedMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) const [actualMedianRate] = await sortedOracles.medianRate(aToken) assertEqualBN(actualMedianRate, newExpectedMedianRate) }) it('should not change the number of total reports', async () => { const initialNumReports = await sortedOracles.numRates(aToken) - await sortedOracles.report( - aToken, - toFixed(new BigNumber(newMedianRate).dividedBy(new BigNumber(denominator))), - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, newExpectedMedianRate, NULL_ADDRESS, NULL_ADDRESS, { + from: anOracle, + }) assertEqualBN(initialNumReports, await sortedOracles.numRates(aToken)) }) @@ -419,25 +354,22 @@ contract('SortedOracles', (accounts: string[]) => { const anOracleMedianRate2 = 3 const anotherOracleMedianRate = 1 - const anOracleExpectedMedianRate1 = expectedMedianRateFromGiven( - anOracleMedianRate1, - denominator + const anOracleExpectedMedianRate1 = toFixed( + new BigNumber(anOracleMedianRate1).dividedBy(new BigNumber(1)) ) - const anOracleExpectedMedianRate2 = expectedMedianRateFromGiven( - anOracleMedianRate2, - denominator + const anOracleExpectedMedianRate2 = toFixed( + new BigNumber(anOracleMedianRate2).dividedBy(new BigNumber(1)) ) - const anotherOracleExpectedMedianRate = expectedMedianRateFromGiven( - anotherOracleMedianRate, - denominator + const anotherOracleExpectedMedianRate = toFixed( + new BigNumber(anotherOracleMedianRate).dividedBy(new BigNumber(1)) ) beforeEach(async () => { sortedOracles.addOracle(aToken, anotherOracle) await sortedOracles.report( aToken, - toFixed(new BigNumber(anotherOracleMedianRate).dividedBy(new BigNumber(denominator))), + anotherOracleExpectedMedianRate, NULL_ADDRESS, NULL_ADDRESS, { @@ -447,7 +379,7 @@ contract('SortedOracles', (accounts: string[]) => { await timeTravel(5, web3) await sortedOracles.report( aToken, - toFixed(new BigNumber(anOracleMedianRate1).dividedBy(new BigNumber(denominator))), + anOracleExpectedMedianRate1, anotherOracle, NULL_ADDRESS, { @@ -465,7 +397,7 @@ contract('SortedOracles', (accounts: string[]) => { it('updates the list of rates correctly', async () => { await sortedOracles.report( aToken, - toFixed(new BigNumber(anOracleMedianRate2).dividedBy(new BigNumber(denominator))), + anOracleExpectedMedianRate2, anotherOracle, NULL_ADDRESS, { @@ -479,15 +411,9 @@ contract('SortedOracles', (accounts: string[]) => { it('updates the latest timestamp', async () => { const initialTimestamps = await sortedOracles.getTimestamps(aToken) - await sortedOracles.report( - aToken, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), - anotherOracle, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, givenMedianRate, anotherOracle, NULL_ADDRESS, { + from: anOracle, + }) const resultTimestamps = await sortedOracles.getTimestamps(aToken) // the second timestamp, belonging to anotherOracle should be unchanged diff --git a/packages/protocol/test/stability/stabletoken.ts b/packages/protocol/test/stability/stabletoken.ts index 863f39c4489..d3cf0d10180 100644 --- a/packages/protocol/test/stability/stabletoken.ts +++ b/packages/protocol/test/stability/stabletoken.ts @@ -13,7 +13,6 @@ import { RegistryInstance, StableTokenInstance } from 'types' const Registry: Truffle.Contract = artifacts.require('Registry') const StableToken: Truffle.Contract = artifacts.require('StableToken') -const EighteenMonthsInSeconds = 47340000 // @ts-ignore // TODO(mcortesi): Use BN.js @@ -26,6 +25,7 @@ contract('StableToken', (accounts: string[]) => { const amountToMint = 10 const SECONDS_IN_A_WEEK = 60 * 60 * 24 * 7 + const EIGHTEEN_MONTHS_IN_SECONDS = 47340000 beforeEach(async () => { registry = await Registry.new() @@ -79,7 +79,7 @@ contract('StableToken', (accounts: string[]) => { assert.isTrue(rate.eq(fixed1)) assert.isTrue(factor.eq(fixed1)) assert.equal(updatePeriod.toNumber(), SECONDS_IN_A_WEEK) - assert.equal(factorLastUpdated.toNumber(), initializationTime + EighteenMonthsInSeconds) + assert.equal(factorLastUpdated.toNumber(), initializationTime + EIGHTEEN_MONTHS_IN_SECONDS) }) it('should not be callable again', async () => { @@ -178,8 +178,8 @@ contract('StableToken', (accounts: string[]) => { const inflationRate = toFixed(201 / 200) beforeEach(async () => { await stableToken.setInflationParameters(inflationRate, SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) }) it('should update factor', async () => { @@ -188,7 +188,7 @@ contract('StableToken', (accounts: string[]) => { assert.isTrue(factor.eq(inflationRate)) assert.equal( lastUpdated.toNumber(), - initializationTime + EighteenMonthsInSeconds + updatePeriod.toNumber() + initializationTime + EIGHTEEN_MONTHS_IN_SECONDS + updatePeriod.toNumber() ) }) @@ -198,7 +198,7 @@ contract('StableToken', (accounts: string[]) => { event: 'InflationFactorUpdated', args: { factor: inflationRate, - lastUpdated: initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds, + lastUpdated: initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS, }, }) }) @@ -213,7 +213,7 @@ contract('StableToken', (accounts: string[]) => { const [rate, , updatePeriod, lastUpdated] = await stableToken.getInflationParameters() assert.isTrue(rate.eq(inflationRate)) assert.equal(updatePeriod.toNumber(), newUpdatePeriod) - assert.equal(lastUpdated.toNumber(), initializationTime + EighteenMonthsInSeconds) + assert.equal(lastUpdated.toNumber(), initializationTime + EIGHTEEN_MONTHS_IN_SECONDS) }) it('should emit an InflationParametersUpdated event', async () => { @@ -235,8 +235,8 @@ contract('StableToken', (accounts: string[]) => { const expectedFactor = toFixed(3 / 2) const newRate = toFixed(1) await stableToken.setInflationParameters(initialRate, SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) const res = await stableToken.setInflationParameters(newRate, SECONDS_IN_A_WEEK) const [rate, factor, , lastUpdated] = await stableToken.getInflationParameters() assertLogMatches2(res.logs[0], { @@ -278,8 +278,8 @@ contract('StableToken', (accounts: string[]) => { describe('#when there is 0.5% weekly inflation', () => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) }) it('should return depreciated balance value', async () => { @@ -292,8 +292,8 @@ contract('StableToken', (accounts: string[]) => { describe('#valueToUnits()', () => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) }) it('value 995 should correspond to roughly 1000 units after .005 depreciation', async () => { @@ -313,8 +313,8 @@ contract('StableToken', (accounts: string[]) => { describe('#unitsToValue()', () => { beforeEach(async () => { await stableToken.setInflationParameters(toFixed(1005 / 1000), SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) }) it('1000 in units should be 995 in value after .005 depreciation', async () => { @@ -392,8 +392,8 @@ contract('StableToken', (accounts: string[]) => { await registry.setAddressFor(CeloContractName.Exchange, sender) await stableToken.mint(sender, amount.times(2)) await stableToken.setInflationParameters(inflationRate, SECONDS_IN_A_WEEK) + await timeTravel(EIGHTEEN_MONTHS_IN_SECONDS, web3) await timeTravel(SECONDS_IN_A_WEEK, web3) - await timeTravel(EighteenMonthsInSeconds, web3) }) async function assertInflationUpdatedEvent(log, requestBlockTime, inflationPeriods = 1) { @@ -407,7 +407,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.setInflationParameters(fixed1, SECONDS_IN_A_WEEK) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) @@ -415,7 +415,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.approve(receiver, amount) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) @@ -423,7 +423,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.mint(sender, amountToMint) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) @@ -431,7 +431,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.transferWithComment(receiver, amount, 'hi') await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) @@ -439,7 +439,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.mint(sender, amount) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) @@ -449,7 +449,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.transferFrom(sender, receiver, amount, { from: receiver }) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK * 2 + EighteenMonthsInSeconds, + initializationTime + SECONDS_IN_A_WEEK * 2 + EIGHTEEN_MONTHS_IN_SECONDS, 2 ) }) @@ -458,7 +458,7 @@ contract('StableToken', (accounts: string[]) => { const res = await stableToken.transfer(receiver, 1) await assertInflationUpdatedEvent( res.logs[0], - initializationTime + SECONDS_IN_A_WEEK + EighteenMonthsInSeconds + initializationTime + SECONDS_IN_A_WEEK + EIGHTEEN_MONTHS_IN_SECONDS ) }) }) From 8028b05814b5a7801a473d228b213475214849bc Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Wed, 22 Jan 2020 17:28:46 +0200 Subject: [PATCH 09/11] GasPriceMinimum namings -> rateNumerator to rate, sortedoracles unit tests small assertion correction --- packages/protocol/contracts/common/GasPriceMinimum.sol | 9 +++------ packages/protocol/contracts/stability/SortedOracles.sol | 7 +++++-- packages/protocol/test/governance/epochrewards.ts | 2 +- packages/protocol/test/stability/sortedoracles.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/protocol/contracts/common/GasPriceMinimum.sol b/packages/protocol/contracts/common/GasPriceMinimum.sol index 27c0264d1f7..05bffc48967 100644 --- a/packages/protocol/contracts/common/GasPriceMinimum.sol +++ b/packages/protocol/contracts/common/GasPriceMinimum.sol @@ -79,13 +79,10 @@ contract GasPriceMinimum is Ownable, Initializable, UsingRegistry { ISortedOracles sortedOracles = ISortedOracles( registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID) ); - uint256 rateNumerator; + uint256 rate; uint256 rateDenominator; - (rateNumerator, rateDenominator) = sortedOracles.medianRate(tokenAddress); - FixidityLib.Fraction memory ratio = FixidityLib.newFixedFraction( - rateNumerator, - rateDenominator - ); + (rate, rateDenominator) = sortedOracles.medianRate(tokenAddress); + FixidityLib.Fraction memory ratio = FixidityLib.newFixedFraction(rate, rateDenominator); return (FixidityLib.newFixed(gasPriceMinimum)).multiply(ratio).fromFixed(); } } diff --git a/packages/protocol/contracts/stability/SortedOracles.sol b/packages/protocol/contracts/stability/SortedOracles.sol index 905519ad8d0..158980c9400 100644 --- a/packages/protocol/contracts/stability/SortedOracles.sol +++ b/packages/protocol/contracts/stability/SortedOracles.sol @@ -172,7 +172,10 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { * @return The median exchange rate for `token`. */ function medianRate(address token) external view returns (uint256, uint256) { - return (rates[token].getMedianValue(), numRates(token)); + return ( + rates[token].getMedianValue(), + numRates(token) == 0 ? 0 : FixidityLib.fixed1().unwrap() + ); } /** @@ -241,7 +244,7 @@ contract SortedOracles is ISortedOracles, Ownable, Initializable { emit OracleReportRemoved(token, oracle); uint256 newMedian = rates[token].getMedianValue(); if (newMedian != originalMedian) { - emit MedianUpdated(token, newMedian); + emit MedianUpdated(token, newMedian == 0 ? 0 : FixidityLib.fixed1().unwrap()); } } } diff --git a/packages/protocol/test/governance/epochrewards.ts b/packages/protocol/test/governance/epochrewards.ts index 39fb8670fdc..f123b425cc9 100644 --- a/packages/protocol/test/governance/epochrewards.ts +++ b/packages/protocol/test/governance/epochrewards.ts @@ -416,7 +416,7 @@ contract('EpochRewards', (accounts: string[]) => { }) it('should return one', async () => { - assertEqualBN(await epochRewards.getRewardsMultiplier(), toFixed(1)) + assertEqualBN(await epochRewards.getRewardsMultiplier(), toFixed(new BigNumber(1))) }) }) diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index c8fac801137..fa15014d41b 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -274,7 +274,7 @@ contract('SortedOracles', (accounts: string[]) => { }) const [actualMedianRate, numberOfRates] = await sortedOracles.medianRate(aToken) assertEqualBN(actualMedianRate, givenMedianRate) - assertEqualBN(numberOfRates.toNumber(), 1) + assertEqualBN(numberOfRates, toFixed(new BigNumber(1))) }) it('should increase the number of timestamps', async () => { From c833e0c9ba123afc281feaa194043592dd80ba94 Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Thu, 23 Jan 2020 10:22:07 +0200 Subject: [PATCH 10/11] getInternalDenominator removed from SortedOracles.ts wrapper --- packages/contractkit/src/wrappers/SortedOracles.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/contractkit/src/wrappers/SortedOracles.ts b/packages/contractkit/src/wrappers/SortedOracles.ts index 4c662348a0a..1bd184effd5 100644 --- a/packages/contractkit/src/wrappers/SortedOracles.ts +++ b/packages/contractkit/src/wrappers/SortedOracles.ts @@ -57,7 +57,7 @@ export class SortedOraclesWrapper extends BaseWrapper { const tokenAddress = await this.kit.registry.addressFor(token) const response = await this.contract.methods.medianRate(tokenAddress).call() return { - rate: toBigNumber(response[0]).div(toBigNumber(response[1])), + rate: toFixed(toBigNumber(response[0]).div(toBigNumber(response[1]))), } } @@ -149,23 +149,18 @@ export class SortedOraclesWrapper extends BaseWrapper { const tokenAddress = await this.kit.registry.addressFor(token) const response = await this.contract.methods.getRates(tokenAddress).call() const rates: OracleRate[] = [] - const denominator = await this.getInternalDenominator() for (let i = 0; i < response[0].length; i++) { const medRelIndex = parseInt(response[2][i], 10) rates.push({ address: response[0][i], - rate: toBigNumber(response[1][i]).div(denominator), + rate: toFixed(toBigNumber(response[1][i]).div(toBigNumber(response[2][i]))), medianRelation: medRelIndex, }) } return rates } - private async getInternalDenominator(): Promise { - return toBigNumber(await this.contract.methods.getDenominator().call()) - } - private async findLesserAndGreaterKeys( token: CeloToken, numerator: number, From 661043b8f65491329cf28903c1bc77a5c1839d04 Mon Sep 17 00:00:00 2001 From: Ventsislav Tsochev Date: Mon, 27 Jan 2020 13:54:36 +0200 Subject: [PATCH 11/11] unnecessary bigNumber parse removed --- .../contracts/common/GasPriceMinimum.sol | 11 +-- .../scripts/truffle/set_exchange_rate.ts | 2 +- .../protocol/test/governance/epochrewards.ts | 2 +- .../protocol/test/stability/sortedoracles.ts | 70 +++++-------------- 4 files changed, 28 insertions(+), 57 deletions(-) diff --git a/packages/protocol/contracts/common/GasPriceMinimum.sol b/packages/protocol/contracts/common/GasPriceMinimum.sol index 05bffc48967..2ab8f3ab13e 100644 --- a/packages/protocol/contracts/common/GasPriceMinimum.sol +++ b/packages/protocol/contracts/common/GasPriceMinimum.sol @@ -79,11 +79,14 @@ contract GasPriceMinimum is Ownable, Initializable, UsingRegistry { ISortedOracles sortedOracles = ISortedOracles( registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID) ); - uint256 rate; + uint256 rateNumerator; uint256 rateDenominator; - (rate, rateDenominator) = sortedOracles.medianRate(tokenAddress); - FixidityLib.Fraction memory ratio = FixidityLib.newFixedFraction(rate, rateDenominator); - return (FixidityLib.newFixed(gasPriceMinimum)).multiply(ratio).fromFixed(); + (rateNumerator, rateDenominator) = sortedOracles.medianRate(tokenAddress); + FixidityLib.Fraction memory rate = FixidityLib.newFixedFraction( + rateNumerator, + rateDenominator + ); + return (FixidityLib.newFixed(gasPriceMinimum)).multiply(rate).fromFixed(); } } diff --git a/packages/protocol/scripts/truffle/set_exchange_rate.ts b/packages/protocol/scripts/truffle/set_exchange_rate.ts index e91bf8694cf..e19d3ba1067 100644 --- a/packages/protocol/scripts/truffle/set_exchange_rate.ts +++ b/packages/protocol/scripts/truffle/set_exchange_rate.ts @@ -136,7 +136,7 @@ module.exports = async (callback: (error?: any) => number) => { // Report it await oracles.report( stableToken.address, - toFixed(new BigNumber(numerator).dividedBy(new BigNumber(denominator))), + toFixed(numerator.dividedBy(denominator)), lesserKey, greaterKey ) diff --git a/packages/protocol/test/governance/epochrewards.ts b/packages/protocol/test/governance/epochrewards.ts index f123b425cc9..39fb8670fdc 100644 --- a/packages/protocol/test/governance/epochrewards.ts +++ b/packages/protocol/test/governance/epochrewards.ts @@ -416,7 +416,7 @@ contract('EpochRewards', (accounts: string[]) => { }) it('should return one', async () => { - assertEqualBN(await epochRewards.getRewardsMultiplier(), toFixed(new BigNumber(1))) + assertEqualBN(await epochRewards.getRewardsMultiplier(), toFixed(1)) }) }) diff --git a/packages/protocol/test/stability/sortedoracles.ts b/packages/protocol/test/stability/sortedoracles.ts index fa15014d41b..101e9247819 100644 --- a/packages/protocol/test/stability/sortedoracles.ts +++ b/packages/protocol/test/stability/sortedoracles.ts @@ -117,7 +117,7 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, toFixed(new BigNumber(1)), NULL_ADDRESS, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(1), NULL_ADDRESS, NULL_ADDRESS, { from: anOracle, }) }) @@ -132,7 +132,7 @@ contract('SortedOracles', (accounts: string[]) => { for (let i = 7; i > 3; i--) { const anotherOracle = accounts[i] await sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report(aToken, toFixed(new BigNumber(2)), anOracle, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(2), anOracle, NULL_ADDRESS, { from: anotherOracle, }) } @@ -174,7 +174,7 @@ contract('SortedOracles', (accounts: string[]) => { describe('when a report has been made', () => { beforeEach(async () => { - await sortedOracles.report(aToken, toFixed(new BigNumber(10)), NULL_ADDRESS, NULL_ADDRESS, { + await sortedOracles.report(aToken, toFixed(10), NULL_ADDRESS, NULL_ADDRESS, { from: anOracle, }) }) @@ -256,7 +256,7 @@ contract('SortedOracles', (accounts: string[]) => { }) describe('#report', () => { - const givenMedianRate = toFixed(new BigNumber(5).dividedBy(new BigNumber(1))) + const givenMedianRate = toFixed(5) beforeEach(async () => { await sortedOracles.addOracle(aToken, anOracle) }) @@ -274,7 +274,7 @@ contract('SortedOracles', (accounts: string[]) => { }) const [actualMedianRate, numberOfRates] = await sortedOracles.medianRate(aToken) assertEqualBN(actualMedianRate, givenMedianRate) - assertEqualBN(numberOfRates, toFixed(new BigNumber(1))) + assertEqualBN(numberOfRates, toFixed(1)) }) it('should increase the number of timestamps', async () => { @@ -321,7 +321,7 @@ contract('SortedOracles', (accounts: string[]) => { }) describe('when there exists exactly one other report, made by this oracle', () => { - const newExpectedMedianRate = toFixed(new BigNumber(12).dividedBy(new BigNumber(1))) + const newExpectedMedianRate = toFixed(12) beforeEach(async () => { await sortedOracles.report(aToken, newExpectedMedianRate, NULL_ADDRESS, NULL_ADDRESS, { @@ -350,63 +350,31 @@ contract('SortedOracles', (accounts: string[]) => { describe('when there are multiple reports, the most recent one done by this oracle', () => { const anotherOracle = accounts[6] - const anOracleMedianRate1 = 2 - const anOracleMedianRate2 = 3 - const anotherOracleMedianRate = 1 - - const anOracleExpectedMedianRate1 = toFixed( - new BigNumber(anOracleMedianRate1).dividedBy(new BigNumber(1)) - ) - const anOracleExpectedMedianRate2 = toFixed( - new BigNumber(anOracleMedianRate2).dividedBy(new BigNumber(1)) - ) - - const anotherOracleExpectedMedianRate = toFixed( - new BigNumber(anotherOracleMedianRate).dividedBy(new BigNumber(1)) - ) beforeEach(async () => { sortedOracles.addOracle(aToken, anotherOracle) - await sortedOracles.report( - aToken, - anotherOracleExpectedMedianRate, - NULL_ADDRESS, - NULL_ADDRESS, - { - from: anotherOracle, - } - ) + await sortedOracles.report(aToken, toFixed(1), NULL_ADDRESS, NULL_ADDRESS, { + from: anotherOracle, + }) await timeTravel(5, web3) - await sortedOracles.report( - aToken, - anOracleExpectedMedianRate1, - anotherOracle, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, toFixed(2), anotherOracle, NULL_ADDRESS, { + from: anOracle, + }) await timeTravel(5, web3) // confirm the setup worked const initialRates = await sortedOracles.getRates(aToken) - assertEqualBN(initialRates['1'][0], anOracleExpectedMedianRate1) - assertEqualBN(initialRates['1'][1], anotherOracleExpectedMedianRate) + assertEqualBN(initialRates['1'][0], toFixed(2)) + assertEqualBN(initialRates['1'][1], toFixed(1)) }) it('updates the list of rates correctly', async () => { - await sortedOracles.report( - aToken, - anOracleExpectedMedianRate2, - anotherOracle, - NULL_ADDRESS, - { - from: anOracle, - } - ) + await sortedOracles.report(aToken, toFixed(3), anotherOracle, NULL_ADDRESS, { + from: anOracle, + }) const resultRates = await sortedOracles.getRates(aToken) - assertEqualBN(resultRates['1'][0], anOracleExpectedMedianRate2) - assertEqualBN(resultRates['1'][1], anotherOracleExpectedMedianRate) + assertEqualBN(resultRates['1'][0], toFixed(3)) + assertEqualBN(resultRates['1'][1], toFixed(1)) }) it('updates the latest timestamp', async () => {