Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implementation of tickets 1604, 1597 and 1365 #2368

Closed

Conversation

BatiGencho
Copy link
Contributor

@BatiGencho BatiGencho commented Jan 6, 2020

Description

Implementation of tickets 1604, 1597 and 1365

Related issues

Note

Regarding SortedOracles.sol, we are changing the type of values stored in the state variable r"ates" to unwrapped Fixidity fractions meaning those changes are not backwards compatible and shall remain as so onwards. The latter is to take effect on all interacting package levels.

@BatiGencho BatiGencho requested a review from mcortesi January 6, 2020 14:52
@jfoutts-celo jfoutts-celo self-assigned this Jan 7, 2020
packages/protocol/contracts/common/MultiSig.sol Outdated Show resolved Hide resolved
packages/protocol/test/stability/sortedoracles.ts Outdated Show resolved Hide resolved
packages/protocol/test/stability/sortedoracles.ts Outdated Show resolved Hide resolved
packages/protocol/test/stability/sortedoracles.ts Outdated Show resolved Hide resolved
@@ -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
Copy link
Contributor

@jfoutts-celo jfoutts-celo Jan 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It this comment accurate? It seems value is also expected to have been multiplied by getDenominator(). Is this what we want? Now that we're using Fixidity, could we keep the internal denominator private and unexposed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

param description fixed, internal denominator is private, but I have added a getter needed by the cli and contractskit

* @notice Returns the common fixidity denominator.
* @return The common fixidity denominator.
*/
function getDenominator() public view returns (uint256) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have the int. denominator as private, I have added a public method that is to be used by external sources e.g. contractskit and cli. Please let me know if any objections to that

Copy link
Contributor

@mcortesi mcortesi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed changes for 1604

@@ -109,15 +110,34 @@ contract MultiSig is Initializable {
initializer
validRequirement(_owners.length, _required)
{
setInitialOwners(_owners);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea here is to call addOwner and changeRequirement instead of creating new setters that will only be used on initialize.

And since those functions are external probably makes sense to have an _addOwner internal function that both initialize and addOwner calls. Some for the other function

* @return True upon success.
* @dev unwrapped Fixidity value expected for _targetVotingYield
*/
function setTargetVotingYield(uint256 _targetVotingYield) public onlyOwner returns (bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure for this contract, but we are changing the semantics here... since dureing the lifetime of the contract we can change the target voting yield.
I wonder if that's what we want.

@asaj @marekolszewski comments?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, the scope of this ticket was more about reducing tech debt than exposing setters for values that were previously not possible to set externally

* @param _startTime The time in unix.
* @return True upon success.
*/
function setStartTime(uint256 _startTime) public onlyOwner returns (bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same
@asaj @marekolszewski comments?

* @notice Updates the last dequeue timestamp.
* @param _lastDequeue The last dequeue timestamp.
*/
function setLastDequeue(uint256 _lastDequeue) public onlyOwner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same...
@asaj @marekolszewski comments?

* @notice Setter for the total supply.
* @param totalSupply The total supply to set.
*/
function setTotalSupply(uint256 totalSupply) public {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want a public function that lets you change the total supply

@asaj @marekolszewski comments?

* @notice Setter for the decimals of the token.
* @param decimals The decimals to set.
*/
function setDecimals(uint8 decimals) public {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here... you can't change decimal at half of the lifecycle

* @notice Setter for the name of the token.
* @param name The name to set.
*/
function setName(string memory name) public {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about changing name or symbol during the lifecycle?

@asaj @marekolszewski comments?

Copy link
Contributor

@mcortesi mcortesi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BatiGencho where's 1597 implemented??

@@ -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, premultiplied with the denominator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of #1365 is not to premultiply with denominator, but instead make users report rates as a fractional number (encoded as a fixidity number).

So if rate is 1, it should be encoded as 1000000000000000000000000
and if it's 0.5 it should be 500000000000000000000000

// All oracle rates are assumed to have a denominator of 2 ^ 64.
uint256 public constant DENOMINATOR = 0x10000000000000000;
uint256 private constant DENOMINATOR = 0x10000000000000000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thus, this should be removed

Ventsislav Tsochev and others added 5 commits January 14, 2020 15:16
…nt naming fixes,

EpochRewards -> few setters switched to privet,
Governance -> setLastDequeue() switched to private,
StableToken -> setters for name, symbol, and decimals removed
… limechain/t_1604_1597_1365

# Conflicts:
#	packages/protocol/contracts/common/MultiSig.sol
numerator, denominator replaced with medianValue,
unit test update
@@ -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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/ratio/rate

@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

@@ -182,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 : DENOMINATOR);
return (rates[token].getMedianValue(), numRates(token));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels dangerous to change the behavior here unless you're 100% certain you've made changes everywhere the output of this function is consumed

await sortedOracles.report(
stableToken.address,
config.stableToken.goldPrice,
1,
new BigNumber(config.stableToken.goldPrice / 1),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we multiply by 10^24? i.e. use toFixed here?

await oracles.report(stableToken.address, numerator, denominator, lesserKey, greaterKey)
await oracles.report(
stableToken.address,
numerator.dividedBy(denominator),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, we need to use toFixed here

@@ -115,7 +117,9 @@ 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), NULL_ADDRESS, NULL_ADDRESS, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want toFixed(1) here

@@ -128,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, 2, 1, anOracle, NULL_ADDRESS, {
await sortedOracles.report(aToken, new BigNumber(2), anOracle, NULL_ADDRESS, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toFixed(2)

@@ -170,7 +174,7 @@ contract('SortedOracles', (accounts: string[]) => {

describe('when a report has been made', () => {
beforeEach(async () => {
await sortedOracles.report(aToken, 10, 1, NULL_ADDRESS, NULL_ADDRESS, {
await sortedOracles.report(aToken, new BigNumber(10), NULL_ADDRESS, NULL_ADDRESS, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toFixed(10)

}

const numerator = 10
const numerator = 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to define these as numerator and denominator now, you can just use a single "rate"

@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider time traveling the eighteen months before the wek

@asaj asaj removed their assignment Jan 22, 2020
Ventsislav Tsochev added 3 commits January 22, 2020 13:50
…rate.ts the median rate is multiplied by 10^24 (toFixed), sortedoracles.ts (unittests) numerator / denomerator replaced with rate
sortedoracles unit tests small assertion correction
@@ -79,13 +79,10 @@ contract GasPriceMinimum is Ownable, Initializable, UsingRegistry {
ISortedOracles sortedOracles = ISortedOracles(
registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID)
);
uint256 rateNumerator;
uint256 rate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer rateNumerator

@@ -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))),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to make these bignumbers, you should be able to do toFixed(numerator / denominator)

@@ -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)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, toFixed(1)

@@ -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, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -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, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -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, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

const numerator = 5
const denominator = 1
const expectedMedianRate = expectedMedianRateFromGiven(numerator, denominator)
const givenMedianRate = toFixed(new BigNumber(5).dividedBy(new BigNumber(1)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, toFixed(5)

assertEqualBN(actualMedianRate, expectedMedianRate)
assertEqualBN(numberOfRates.toNumber(), 1)
assertEqualBN(actualMedianRate, givenMedianRate)
assertEqualBN(numberOfRates, toFixed(new BigNumber(1)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

})

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)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto toFixed(12)

const anOracleExpectedMedianRate1 = expectedMedianRateFromGiven(
anOracleMedianRate1,
denominator
const anOracleExpectedMedianRate1 = toFixed(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these variables necessary anymore?

@asaj asaj assigned asaj and unassigned BatiGencho Feb 22, 2020
@asaj asaj added the audit label Feb 22, 2020
@asaj
Copy link
Contributor

asaj commented Feb 23, 2020

Closing as there are open PRs that address these issues individually.

@asaj asaj closed this Feb 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Internal audit of parameter validation in contract initialization
4 participants