Skip to content

Commit

Permalink
fix(StorageRent): add price feed max age (#277)
Browse files Browse the repository at this point in the history
  • Loading branch information
horsefacts authored Jul 26, 2023
1 parent 2c6fa9b commit 692acb0
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 10 deletions.
14 changes: 7 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
BundleRegistryGasUsageTest:testGasRegisterWithSig() (gas: 817615)
BundleRegistryGasUsageTest:testGasTrustedBatchRegister() (gas: 3493661)
BundleRegistryGasUsageTest:testGasTrustedRegister() (gas: 509143)
BundleRegistryGasUsageTest:testGasRegisterWithSig() (gas: 817210)
BundleRegistryGasUsageTest:testGasTrustedBatchRegister() (gas: 3495861)
BundleRegistryGasUsageTest:testGasTrustedRegister() (gas: 509363)
IdRegistryGasUsageTest:testGasRegister() (gas: 735324)
IdRegistryGasUsageTest:testGasRegisterForAndRecover() (gas: 1698920)
IdRegistryGasUsageTest:testGasRegisterFromTrustedCaller() (gas: 806961)
StorageRentGasUsageTest:testGasBatchCredit() (gas: 169333)
StorageRentGasUsageTest:testGasBatchRent() (gas: 267476)
StorageRentGasUsageTest:testGasBatchCredit() (gas: 168903)
StorageRentGasUsageTest:testGasBatchRent() (gas: 267026)
StorageRentGasUsageTest:testGasContinuousCredit() (gas: 142400)
StorageRentGasUsageTest:testGasCredit() (gas: 78170)
StorageRentGasUsageTest:testGasRent() (gas: 159716)
StorageRentGasUsageTest:testGasCredit() (gas: 78390)
StorageRentGasUsageTest:testGasRent() (gas: 159266)
27 changes: 27 additions & 0 deletions src/StorageRent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ contract StorageRent is AccessControlEnumerable {
*/
event SetCacheDuration(uint256 oldDuration, uint256 newDuration);

/**
* @dev Emit an event when an admin changes the priceFeedMaxAge.
*
* @param oldAge The previous priceFeedMaxAge.
* @param newAge The new priceFeedMaxAge.
*/
event SetMaxAge(uint256 oldAge, uint256 newAge);

/**
* @dev Emit an event when an admin changes the uptimeFeedGracePeriod.
*
Expand Down Expand Up @@ -214,6 +222,11 @@ contract StorageRent is AccessControlEnumerable {
*/
uint256 public priceFeedCacheDuration;

/**
* @dev Max age of a price feed answer before it is considered stale. Changeable by admin.
*/
uint256 public priceFeedMaxAge;

/**
* @dev Period in seconds to wait after the L2 sequencer restarts before resuming rentals.
* See: https://docs.chain.link/data-feeds/l2-sequencer-feeds. Changeable by admin.
Expand Down Expand Up @@ -299,6 +312,9 @@ contract StorageRent is AccessControlEnumerable {
priceFeedCacheDuration = 1 days;
emit SetCacheDuration(0, 1 days);

priceFeedMaxAge = 2 hours;
emit SetMaxAge(0, 2 hours);

uptimeFeedGracePeriod = 1 hours;
emit SetGracePeriod(0, 1 hours);

Expand Down Expand Up @@ -522,6 +538,7 @@ contract StorageRent is AccessControlEnumerable {
if (answer <= 0) revert InvalidPrice();
if (priceUpdatedAt == 0) revert IncompleteRound();
if (priceAnsweredInRound < priceRoundId) revert StaleAnswer();
if (block.timestamp - priceUpdatedAt > priceFeedMaxAge) revert StaleAnswer();

/* Set the last update timestamp and block. */
lastPriceFeedUpdateTime = block.timestamp;
Expand Down Expand Up @@ -668,6 +685,16 @@ contract StorageRent is AccessControlEnumerable {
priceFeedCacheDuration = duration;
}

/**
* @notice Change the priceFeedMaxAge. Only callable by admin.
*
* @param age The new priceFeedMaxAge.
*/
function setMaxAge(uint256 age) external onlyAdmin {
emit SetMaxAge(priceFeedMaxAge, age);
priceFeedMaxAge = age;
}

/**
* @notice Change the uptimeFeedGracePeriod. Only callable by admin.
*
Expand Down
53 changes: 52 additions & 1 deletion test/StorageRent/StorageRent.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ contract StorageRentTest is StorageRentTestSuite {
event SetMaxUnits(uint256 oldMax, uint256 newMax);
event SetDeprecationTimestamp(uint256 oldTimestamp, uint256 newTimestamp);
event SetCacheDuration(uint256 oldDuration, uint256 newDuration);
event SetMaxAge(uint256 oldAge, uint256 newAge);
event SetGracePeriod(uint256 oldPeriod, uint256 newPeriod);
event SetVault(address oldVault, address newVault);
event Withdraw(address indexed to, uint256 amount);
Expand Down Expand Up @@ -92,6 +93,10 @@ contract StorageRentTest is StorageRentTestSuite {
assertEq(storageRent.priceFeedCacheDuration(), INITIAL_PRICE_FEED_CACHE_DURATION);
}

function testPriceFeedMaxAgeDefault() public {
assertEq(storageRent.priceFeedMaxAge(), INITIAL_PRICE_FEED_MAX_AGE);
}

function testUptimeFeedGracePeriodDefault() public {
assertEq(storageRent.uptimeFeedGracePeriod(), INITIAL_UPTIME_FEED_GRACE_PERIOD);
}
Expand Down Expand Up @@ -864,7 +869,7 @@ contract StorageRentTest is StorageRentTestSuite {
storageRent.refreshPrice();
}

function testPriceFeedRevertsStaleAnswer() public {
function testPriceFeedRevertsStaleAnswerByRound() public {
// Set stale answeredInRound value
priceFeed.setRoundData(
MockChainlinkFeed.RoundData({
Expand All @@ -881,6 +886,26 @@ contract StorageRentTest is StorageRentTestSuite {
storageRent.refreshPrice();
}

function testPriceFeedRevertsStaleAnswerByMaxAge() public {
vm.warp(INITIAL_PRICE_FEED_MAX_AGE + 2);

// Set stale answeredInRound value
priceFeed.setRoundData(
MockChainlinkFeed.RoundData({
roundId: 1,
answer: 2000e8,
startedAt: block.timestamp,
timeStamp: 1,
answeredInRound: 1
})
);
priceFeed.setStubTimeStamp(true);

vm.expectRevert(StorageRent.StaleAnswer.selector);
vm.prank(admin);
storageRent.refreshPrice();
}

function testPriceFeedRevertsIncompleteRound() public {
// Set zero timeStamp value
priceFeed.setRoundData(
Expand All @@ -892,6 +917,7 @@ contract StorageRentTest is StorageRentTestSuite {
answeredInRound: 1
})
);
priceFeed.setStubTimeStamp(true);
vm.expectRevert(StorageRent.IncompleteRound.selector);
vm.prank(admin);
storageRent.refreshPrice();
Expand Down Expand Up @@ -989,6 +1015,7 @@ contract StorageRentTest is StorageRentTestSuite {
answeredInRound: 1
})
);
uptimeFeed.setStubTimeStamp(true);
vm.expectRevert(StorageRent.IncompleteRound.selector);
vm.prank(admin);
storageRent.refreshPrice();
Expand Down Expand Up @@ -1390,6 +1417,30 @@ contract StorageRentTest is StorageRentTestSuite {
assertEq(storageRent.priceFeedCacheDuration(), duration);
}

/*//////////////////////////////////////////////////////////////
SET MAX PRICE AGE
//////////////////////////////////////////////////////////////*/

function testFuzzOnlyAdminCanSetMaxAge(address caller, uint256 age) public {
vm.assume(caller != admin);

vm.prank(caller);
vm.expectRevert(StorageRent.NotAdmin.selector);
storageRent.setMaxAge(age);
}

function testFuzzSetMaxAge(uint256 age) public {
uint256 currentAge = storageRent.priceFeedMaxAge();

vm.expectEmit(false, false, false, true);
emit SetMaxAge(currentAge, age);

vm.prank(admin);
storageRent.setMaxAge(age);

assertEq(storageRent.priceFeedMaxAge(), age);
}

/*//////////////////////////////////////////////////////////////
SET GRACE PERIOD
//////////////////////////////////////////////////////////////*/
Expand Down
1 change: 1 addition & 0 deletions test/StorageRent/StorageRentTestSuite.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ abstract contract StorageRentTestSuite is TestSuiteSetup {
int256 internal constant ETH_USD_PRICE = 2000e8; // $2000 USD/ETH

uint256 internal constant INITIAL_PRICE_FEED_CACHE_DURATION = 1 days;
uint256 internal constant INITIAL_PRICE_FEED_MAX_AGE = 2 hours;
uint256 internal constant INITIAL_UPTIME_FEED_GRACE_PERIOD = 1 hours;
uint256 internal constant INITIAL_PRICE_IN_ETH = 0.0025 ether;

Expand Down
14 changes: 12 additions & 2 deletions test/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ contract MockChainlinkFeed is AggregatorV3Interface {
uint256 public version = 1;

bool public shouldRevert;
bool public stubTimeStamp;

constructor(uint8 _decimals, string memory _description) {
decimals = _decimals;
Expand All @@ -141,6 +142,10 @@ contract MockChainlinkFeed is AggregatorV3Interface {
shouldRevert = _shouldRevert;
}

function setStubTimeStamp(bool _stubTimeStamp) external {
stubTimeStamp = _stubTimeStamp;
}

function setAnswer(int256 value) external {
roundData.answer = value;
}
Expand All @@ -155,8 +160,13 @@ contract MockChainlinkFeed is AggregatorV3Interface {

function latestRoundData() public view returns (uint80, int256, uint256, uint256, uint80) {
if (shouldRevert) revert("MockChainLinkFeed: Call failed");
return
(roundData.roundId, roundData.answer, roundData.startedAt, roundData.timeStamp, roundData.answeredInRound);
return (
roundData.roundId,
roundData.answer,
roundData.startedAt,
stubTimeStamp ? roundData.timeStamp : block.timestamp,
roundData.answeredInRound
);
}
}

Expand Down

0 comments on commit 692acb0

Please sign in to comment.