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

feat: add missing access controls #252

Closed
wants to merge 1 commit into from
Closed
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
18 changes: 18 additions & 0 deletions contracts/oracles/breakers/MedianDeltaBreaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ contract MedianDeltaBreaker is IBreaker, WithCooldown, WithThreshold, Ownable {

/* ==================== Events ==================== */
event SmoothingFactorSet(address rateFeedId, uint256 smoothingFactor);
event BreakerBoxUpdated(address breakerBox);

/* ==================== State Variables ==================== */
// Address of the Mento SortedOracles contract
ISortedOracles public sortedOracles;

// Address of the BreakerBox contract
address public breakerBox;

// Default smoothing factor for EMA as a Fixidity value
uint256 public constant DEFAULT_SMOOTHING_FACTOR = 1e24;

Expand All @@ -43,12 +47,14 @@ contract MedianDeltaBreaker is IBreaker, WithCooldown, WithThreshold, Ownable {
uint256 _defaultCooldownTime,
uint256 _defaultRateChangeThreshold,
ISortedOracles _sortedOracles,
address _breakerBox,
address[] memory rateFeedIDs,
uint256[] memory rateChangeThresholds,
uint256[] memory cooldownTimes
) public {
_transferOwnership(msg.sender);
setSortedOracles(_sortedOracles);
setBreakerBox(_breakerBox);

_setDefaultCooldownTime(_defaultCooldownTime);
_setDefaultRateChangeThreshold(_defaultRateChangeThreshold);
Expand Down Expand Up @@ -107,6 +113,16 @@ contract MedianDeltaBreaker is IBreaker, WithCooldown, WithThreshold, Ownable {
emit SortedOraclesUpdated(address(_sortedOracles));
}

/**
* @notice Sets the address of the BreakerBox contract.
* @param _breakerBox The new address of the breaker box contract.
*/
function setBreakerBox(address _breakerBox) public onlyOwner {
require(_breakerBox != address(0), "BreakerBox address must be set");
breakerBox = _breakerBox;
emit BreakerBoxUpdated(_breakerBox);
}

/*
* @notice Sets the smoothing factor for a rate feed.
* @param rateFeedID The rate feed to be updated.
Expand Down Expand Up @@ -143,6 +159,8 @@ contract MedianDeltaBreaker is IBreaker, WithCooldown, WithThreshold, Ownable {
* should be tripped for the rate feed.
*/
function shouldTrigger(address rateFeedID) public returns (bool triggerBreaker) {
require(msg.sender == breakerBox, "Only the BreakerBox can call this function");

(uint256 currentMedian, ) = sortedOracles.medianRate(rateFeedID);

uint256 previousRatesEMA = medianRatesEMA[rateFeedID];
Expand Down
2 changes: 1 addition & 1 deletion slither.db.json

Large diffs are not rendered by default.

63 changes: 52 additions & 11 deletions test/oracles/breakers/MedianDeltaBreaker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract MedianDeltaBreakerTest is BaseTest {
address rateFeedID1;
address rateFeedID2;
address rateFeedID3;
address breakerBox;
MockSortedOracles sortedOracles;
MedianDeltaBreaker breaker;

Expand All @@ -38,12 +39,14 @@ contract MedianDeltaBreakerTest is BaseTest {
event SortedOraclesUpdated(address newSortedOracles);
event RateChangeThresholdUpdated(address rateFeedID1, uint256 rateChangeThreshold);
event SmoothingFactorSet(address rateFeedId, uint256 newSmoothingFactor);
event BreakerBoxUpdated(address newBreakerBox);

function setUp() public {
notDeployer = actor("notDeployer");
rateFeedID1 = actor("rateFeedID1");
rateFeedID2 = actor("rateFeedID2");
rateFeedID3 = actor("rateFeedID3");
breakerBox = actor("breakerBox");

rateFeedIDs[0] = rateFeedID2;
rateChangeThresholds[0] = 0.9 * 10**24;
Expand All @@ -60,6 +63,7 @@ contract MedianDeltaBreakerTest is BaseTest {
defaultCooldownTime,
defaultThreshold,
ISortedOracles(address(sortedOracles)),
breakerBox,
rateFeedIDs,
rateChangeThresholds,
cooldownTimes
Expand All @@ -86,6 +90,10 @@ contract MedianDeltaBreakerTest_constructorAndSetters is MedianDeltaBreakerTest
assertEq(address(breaker.sortedOracles()), address(sortedOracles));
}

function test_constructor_shouldSetBreakerBox() public {
assertEq(breaker.breakerBox(), breakerBox);
}

function test_constructor_shouldSetRateChangeThresholds() public {
assertEq(breaker.rateChangeThreshold(rateFeedIDs[0]), rateChangeThresholds[0]);
}
Expand Down Expand Up @@ -152,6 +160,27 @@ contract MedianDeltaBreakerTest_constructorAndSetters is MedianDeltaBreakerTest
assertEq(address(breaker.sortedOracles()), newSortedOracles);
}

function test_setBreakerBox_whenSenderIsNotOwner_shouldRevert() public {
changePrank(notDeployer);
vm.expectRevert("Ownable: caller is not the owner");
breaker.setBreakerBox(address(0));
}

function test_setBreakerBox_whenAddressIsZero_shouldRevert() public {
vm.expectRevert("BreakerBox address must be set");
breaker.setBreakerBox(address(0));
}

function test_setBreakerBox_whenSenderIsOwner_shouldUpdateAndEmit() public {
address newBreakerBox = actor("newBreakerBox");
vm.expectEmit(true, true, true, true);
emit BreakerBoxUpdated(newBreakerBox);

breaker.setBreakerBox(newBreakerBox);

assertEq(address(breaker.breakerBox()), newBreakerBox);
}

function test_setRateChangeThreshold_whenSenderIsNotOwner_shouldRevert() public {
changePrank(notDeployer);
vm.expectRevert("Ownable: caller is not the owner");
Expand Down Expand Up @@ -221,6 +250,12 @@ contract MedianDeltaBreakerTest_constructorAndSetters is MedianDeltaBreakerTest
}

contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {
function shouldTriggerCalledByBreakerBox(address rateFeedID) public returns (bool) {
changePrank(breakerBox);
return breaker.shouldTrigger(rateFeedID);
changePrank(deployer);
}

function setSortedOraclesMedian(uint256 median) public {
vm.mockCall(
address(sortedOracles),
Expand All @@ -240,10 +275,16 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {
vm.expectCall(address(sortedOracles), abi.encodeWithSelector(sortedOracles.medianRate.selector, _rateFeedID));
}

function test_shouldTrigger_whenSenderIsNotBreakerBox_shouldRevert() public {
vm.expectRevert("Only the BreakerBox can call this function");
breaker.shouldTrigger(rateFeedID1);
changePrank(deployer);
}

function test_shouldTrigger_withDefaultThreshold_shouldTrigger() public {
assertEq(breaker.rateChangeThreshold(rateFeedID1), 0);
updatePreviousEMAByPercent(0.7 * 10**24, rateFeedID1);
assertTrue(breaker.shouldTrigger(rateFeedID1));
assertTrue(shouldTriggerCalledByBreakerBox(rateFeedID1));
}

function test_shouldTrigger_whenThresholdIsLargerThanMedian_shouldNotTrigger() public {
Expand All @@ -254,15 +295,15 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {
breaker.setRateChangeThresholds(rateFeedIDs, rateChangeThresholds);
assertEq(breaker.rateChangeThreshold(rateFeedID1), rateChangeThresholds[0]);

assertFalse(breaker.shouldTrigger(rateFeedID1));
assertFalse(shouldTriggerCalledByBreakerBox(rateFeedID1));
}

function test_shouldTrigger_whithDefaultThreshold_ShouldNotTrigger() public {
assertEq(breaker.rateChangeThreshold(rateFeedID3), 0);

updatePreviousEMAByPercent(1.1 * 10**24, rateFeedID3);

assertFalse(breaker.shouldTrigger(rateFeedID3));
assertFalse(shouldTriggerCalledByBreakerBox(rateFeedID3));
}

function test_shouldTrigger_whenThresholdIsSmallerThanMedian_ShouldTrigger() public {
Expand All @@ -272,7 +313,7 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {
breaker.setRateChangeThresholds(rateFeedIDs, rateChangeThresholds);
assertEq(breaker.rateChangeThreshold(rateFeedID3), rateChangeThresholds[0]);

assertTrue(breaker.shouldTrigger(rateFeedID3));
assertTrue(shouldTriggerCalledByBreakerBox(rateFeedID3));
}

function test_shouldTrigger_whenFirstMedianIsReported_EMAShouldBeEqual() public {
Expand All @@ -288,7 +329,7 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {
(uint256 afterRate, ) = sortedOracles.medianRate(rateFeed);
assertEq(afterRate, median);

assertFalse(breaker.shouldTrigger(rateFeed));
assertFalse(shouldTriggerCalledByBreakerBox(rateFeed));
assertEq(breaker.medianRatesEMA(rateFeed), median);
}

Expand All @@ -302,12 +343,12 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {

uint256 firstMedian = 1.05 * 10**24;
setSortedOraclesMedian(firstMedian);
assertFalse(breaker.shouldTrigger(rateFeed));
assertFalse(shouldTriggerCalledByBreakerBox(rateFeed));
assertEq(breaker.medianRatesEMA(rateFeed), firstMedian);

uint256 secondMedian = 1.0164 * 10**24;
setSortedOraclesMedian(secondMedian);
bool triggered = breaker.shouldTrigger((rateFeed));
bool triggered = shouldTriggerCalledByBreakerBox(rateFeed);

// 0.1*1.0164 + (1.05 * 0.9) = 1.04664
assertEq(breaker.medianRatesEMA(rateFeed), 1.04664 * 10**24);
Expand All @@ -326,12 +367,12 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {

uint256 firstMedian = 1.05 * 10**24;
setSortedOraclesMedian(firstMedian);
assertFalse(breaker.shouldTrigger(rateFeed));
assertFalse(shouldTriggerCalledByBreakerBox(rateFeed));
assertEq(breaker.medianRatesEMA(rateFeed), firstMedian);

uint256 secondMedian = 1.0836 * 10**24;
setSortedOraclesMedian(secondMedian);
bool triggered = breaker.shouldTrigger((rateFeed));
bool triggered = shouldTriggerCalledByBreakerBox(rateFeed);

// 0.1*1.0836 + (1.05 * 0.9) = 1.05336
assertEq(breaker.medianRatesEMA(rateFeed), 1.05336 * 10**24);
Expand All @@ -352,7 +393,7 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {

for (uint256 i = 0; i < medians.length; i++) {
setSortedOraclesMedian(medians[i]);
breaker.shouldTrigger(rateFeed);
shouldTriggerCalledByBreakerBox(rateFeed);
assertEq(breaker.medianRatesEMA(rateFeed), medians[i]);
}
}
Expand All @@ -378,7 +419,7 @@ contract MedianDeltaBreakerTest_shouldTrigger is MedianDeltaBreakerTest {

for (uint256 i = 0; i < medians.length; i++) {
setSortedOraclesMedian(medians[i]);
breaker.shouldTrigger(rateFeed);
shouldTriggerCalledByBreakerBox(rateFeed);
assertEq(breaker.medianRatesEMA(rateFeed), expectedEMAs[i]);
}
}
Expand Down
1 change: 1 addition & 0 deletions test/utils/IntegrationTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ contract IntegrationTest is BaseTest {
medianDeltaBreakerDefaultCooldown,
medianDeltaBreakerDefaultThreshold,
ISortedOracles(address(sortedOracles)),
address(breakerBox),
medianDeltaBreakerRateFeedIDs,
medianDeltaBreakerRateChangeThresholds,
medianDeltaBreakerCooldownTimes
Expand Down
Loading