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

Add scaffold for Oracles tests #2

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
*/
}
Loading