Skip to content

Commit

Permalink
Merge pull request #2 from mento-protocol/m-chrzan/testing-scaffold
Browse files Browse the repository at this point in the history
Add scaffold for Oracles tests
  • Loading branch information
chapati23 authored Apr 17, 2024
2 parents eeff383 + c1d1631 commit 4bac2d1
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 1 deletion.
104 changes: 104 additions & 0 deletions src/Oracles.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {IOracles} from "./interfaces/IOracles.sol";
import {OracleValue, OracleValueLib} from "./lib/OracleValueLib.sol";

contract Oracles is IOracles {
using OracleValueLib for OracleValue;

struct OracleBuffer {
uint256[100] medians;
OracleBufferInfo bufferInfo;
}

struct OracleBufferInfo {

Check failure on line 15 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

GC: For [ OracleBufferInfo ] struct, packing seems inefficient. Try rearranging to achieve 32bytes slots
uint8 lastIndex;
uint8 windowSize;
bool bufferFull;

Check failure on line 19 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Delete ⏎
OracleValue windowSum;
OracleValue windowAverage;

Check failure on line 22 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Delete ⏎
uint40 latestTimestamp;

Check failure on line 24 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Replace ⏎········uint8·validityFlags;⏎ with ········uint8·validityFlags;
uint8 validityFlags;

uint16 allowedDeviation;
uint8 quorum;
uint8 certaintyThreshold;
uint16 allowedStaleness;
}

mapping (address => OracleBuffer) rateFeeds;

Check failure on line 33 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Delete ·

Check failure on line 33 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Main key parameter in mapping rateFeeds is not named

Check failure on line 33 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Value parameter in mapping rateFeeds is not named

Check warning on line 33 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Explicitly mark visibility of state

Check failure on line 33 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

'rateFeeds' should start with _
function report(address rateFeedId) external {}

Check warning on line 34 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function markStale(address rateFeedId) external {}

Check warning on line 36 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function setWindowSize(address rateFeedId, uint8 windowSize) external {}

Check warning on line 38 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function setAllowedDeviation(
address rateFeedId,
uint16 allowedDeviation
) external {}

Check warning on line 43 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function setQuorum(address rateFeedId, uint8 quorum) external {}

Check warning on line 45 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function setCertaintyThreshold(
address rateFeedId,
uint8 certaintyThreshold
) external {}

Check warning on line 50 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function setAllowedStaleness(
address rateFeedId,
uint16 allowedStaleness
) external {}

Check warning on line 55 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function addRateFeed(
address rateFeedId,
uint8 windowSize,
uint16 allowedDeviation,
uint8 quorum,
uint8 certaintyThreshold,
uint16 allowedStaleness,
address[] calldata dataProviders
) external {}

Check warning on line 65 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function removeRateFeed(address rateFeedId) external {}

Check warning on line 67 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Code contains empty blocks

function addDataProvider(address rateFeedId, address provider) external {}

function removeDataProvider(
address rateFeedId,
address provider
) external {}

function medianRate(
address rateFeedId
) external view returns (uint256 numerator, uint256 denominator) {}

function medianRateUint64(
address rateFeedId
) external view returns (uint64 medianRate) {}

function rateInfo(
address rateFeedId
) external view returns (uint64 medianRate, uint8 validityFlags) {}

function rateFeedParameters(address rateFeedId) external view returns (

Check failure on line 88 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Replace address·rateFeedId)·external·view with ⏎········address·rateFeedId⏎····)⏎········external⏎········view⏎·······
uint8 windowSize,

Check failure on line 89 in src/Oracles.sol

View workflow job for this annotation

GitHub Actions / Foundry project

Insert ····
uint16 allowedDeviation,
uint8 quorum,
uint8 certaintyThreshold,
uint16 allowedStaleness
) {
OracleBufferInfo memory bufferInfo = rateFeeds[rateFeedId].bufferInfo;
return (
bufferInfo.windowSize,
bufferInfo.allowedDeviation,
bufferInfo.quorum,
bufferInfo.certaintyThreshold,
bufferInfo.allowedStaleness
);
}
}
19 changes: 18 additions & 1 deletion src/interfaces/IOracles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,26 @@ interface IOracles {
/**
* @notice Adds a new supported rate feed.
* @param rateFeedId The new rate feed's ID.
* @param windowSize The new rate feed's averaging window size.
* @param allowedDeviation The new rate feed's allowed deviation.
* @param quorum The new rate feed's required minimum number of values per
* report.
* @param certaintyThreshold The new rate feed's required minimum number of
* certain values per report.
* @param allowedStaleness The new rate feed's allowed staleness.
* @param dataProviders The initial set of data providers for the new rate
* feed.
* @dev Only callable by the owner.
*/
function addRateFeed(address rateFeedId) external;
function addRateFeed(
address rateFeedId,
uint8 windowSize,
uint16 allowedDeviation,
uint8 quorum,
uint8 certaintyThreshold,
uint16 allowedStaleness,
address[] calldata dataProviders
) external;

/**
* @notice Removes a rate feed.
Expand Down
226 changes: 226 additions & 0 deletions test/Oracles.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: UNLICENSED
// solhint-disable func-name-mixedcase, gas-strict-inequalities, ordering
pragma solidity ^0.8.24;

import {Test} from "forge-std/Test.sol";
import {Oracles} from "../src/Oracles.sol";

contract OraclesTest is Test {
Oracles oracles;

address aRateFeed;
// solhint-disable-next-line no-empty-blocks
function setUp() virtual public {
oracles = new Oracles();
aRateFeed = address(0x1337);
}
}

contract Oracles_report is OraclesTest {
}

contract Oracles_markStale is OraclesTest {
}

contract Oracles_setWindowSize is OraclesTest {
function testFuzz_setsWindowSize(uint8 windowSize) public {
vm.assume(windowSize != 0 && windowSize <= 100);
oracles.setWindowSize(aRateFeed, windowSize);
(uint8 realWindowSize,,,,) = oracles.rateFeedParameters(aRateFeed);
assertEq(realWindowSize, windowSize);
}

function test_setTo0Fail() public {
// TODO: set the exact expected error
vm.expectRevert();
oracles.setWindowSize(aRateFeed, 0);
}

function testFuzz_setToOver100Fail(uint8 windowSize) public {
vm.assume(windowSize > 100);
// TODO: set the exact expected error
vm.expectRevert();
oracles.setWindowSize(aRateFeed, windowSize);
}

/*
TODO:
- Only owner
More complex test cases, when average needs to be recalculated.
- When buffer not full yet
- decreasing window
- increasing window, there's enough values for new average
- increasing window, there's not enough values for new average
- When buffer full
- decreasing window
- increasing window, there's enough values before index 0
- increasing window, need to wrap around to end of buffer
- setting window to max (100)
*/
}

contract Oracles_setAllowedDeviation is OraclesTest {
function testFuzz_setsAllowedDeviation(uint16 allowedDeviation) public {
oracles.setAllowedDeviation(aRateFeed, allowedDeviation);
(, uint16 realAllowedDeviation,,,) = oracles.rateFeedParameters(aRateFeed);
assertEq(realAllowedDeviation, allowedDeviation);
}

/*
TODO:
- Only owner
Test cases including a follow-up report:
- New report has too much deviation
- New report fits in new deviation
*/
}

contract Oracles_setQuorum is OraclesTest {
function testFuzz_setsQuorum(uint8 quorum) public {
oracles.setQuorum(aRateFeed, quorum);
(,, uint8 realQuorum,,) = oracles.rateFeedParameters(aRateFeed);
assertEq(realQuorum, quorum);
}

/*
TODO:
- Only owner
- Fails when quorum is larger than the number of whitelisted reporters
test cases including a follow-up report:
- New report has quorum
- New report no longer has quorum
*/
}

contract Oracles_setCertaintyThreshold is OraclesTest {
function testFuzz_setsCertaintyThreshold(uint8 certaintyThreshold) public {
oracles.setCertaintyThreshold(aRateFeed, certaintyThreshold);
(,,, uint8 realCertaintyThreshold,) = oracles.rateFeedParameters(aRateFeed);
assertEq(realCertaintyThreshold, certaintyThreshold);
}

/*
TODO:
- Only owner
- Fails when certainty threshold is larger than the number of whitelisted
reporters
- Fails when certainty threshold is larger than quorum
test cases including a follow-up report:
- New report meets the certainty threshold
- New report no longer meets the certainty threshold
*/
}

contract Oracles_setAllowedStaleness is OraclesTest {
function testFuzz_setsAllowedStaleness(uint16 allowedStaleness) public {
oracles.setAllowedStaleness(aRateFeed, allowedStaleness);
(,,,, uint16 realAllowedStaleness) = oracles.rateFeedParameters(aRateFeed);
assertEq(realAllowedStaleness, allowedStaleness);
}

/*
TODO:
- Only owner
- Fails when certainty threshold is shorter than block time
test cases including a follow-up report:
- New report meets the allowed staleness
- The new window is shorter, markStale marks as stale when with the
previous window it would have still been fresh
- The new window is longer, markStale doesn't mark as stale when with
the previous window it would have been
- New report no longer meets the allowed staleness
*/
}

contract Oracles_addRateFeed is OraclesTest {
function test_createsANewRateFeed() public {
address anotherRateFeed = address(0xbeef);
address[] memory dataProviders = new address[](1);
dataProviders[0] = address(0xcafe);
oracles.addRateFeed(
anotherRateFeed,
2,
100,
5,
3,
120,
dataProviders
);

(
uint8 realWindowSize,
uint16 realAllowedDeviation,
uint8 realQuorum,
uint8 realCertaintyThreshold,
uint16 realAllowedStaleness
) = oracles.rateFeedParameters(anotherRateFeed);

assertEq(realWindowSize, 2);
assertEq(realAllowedDeviation, 100);
assertEq(realQuorum, 5);
assertEq(realCertaintyThreshold, 2);
assertEq(realAllowedStaleness, 120);
}

/*
TODO:
- Only owner
- Fails with invalid parameters (e.g. quorum > # providers)
*/
}

contract Oracles_removeRateFeed is OraclesTest {
address anotherRateFeed = address(0xbeef);
address aDataProvider = address(0xcafe);

function setUp() override public {
super.setUp();
address[] memory dataProviders = new address[](1);
dataProviders[0] = address(0xcafe);
oracles.addRateFeed(
anotherRateFeed,
2,
100,
5,
3,
120,
dataProviders
);

(
uint8 realWindowSize,
uint16 realAllowedDeviation,
uint8 realQuorum,
uint8 realCertaintyThreshold,
uint16 realAllowedStaleness
) = oracles.rateFeedParameters(anotherRateFeed);
}

function test_removesTheRateFeed() public {
oracles.removeRateFeed(anotherRateFeed);

(uint8 realWindowSize,,,,) = oracles.rateFeedParameters(anotherRateFeed);
assertEq(realWindowSize, 0);
}

/*
TODO:
- Only owner
*/
}

contract Oracles_addDataProvider is OraclesTest {

/*
TODO:
- Only owner
*/
}

contract Oracles_removeDataProvider is OraclesTest {

/*
TODO:
- Only owner
*/
}

0 comments on commit 4bac2d1

Please sign in to comment.