Assessment reports>Concrete>Medium findings>Incorrect L2 sequencer uptime feed integration
Category: Coding Mistakes

Incorrect L2 sequencer uptime feed integration

Medium Impact
High Severity
Medium Likelihood

Description

When the L2 sequencer feed (_sequencerOracle) is set, the getAssetPrice function in ConcreteOracle checks data freshness from the sequencer feed before consuming data from the price feed:

function getAssetPrice(address asset) public view override returns (uint256) {
	// [...]
		(, int256 price,, uint256 updatedAt,) = source.latestRoundData();
		bool isFreshPrice = updatedAt >= (block.timestamp - getGracePeriodOfAsset(asset));
		if (price > 0 && isFreshPrice && _isSequencerOracleFresh()) {
			return _normalizePrice(source, uint256(price));
		} else {
			return _getAssetPriceFromFallbackOracle(asset);
		}
	// [...]
}

function _isSequencerOracleFresh() internal view returns (bool) {
	// for L1 layers, we always return true
	if (address(_sequencerOracle) == address(0)) return true;
	// for L2 layers, we check if the price is fresh
	(,,, uint256 updatedAt,) = _sequencerOracle.latestRoundData();
	return updatedAt >= (block.timestamp - DEFAULT_GRACE_PERIOD);
}

However, L2 sequencer uptime feeds update only when the sequencer status changes. If the sequencer operates normally and the last update occurred long ago, _isSequencerOracleFresh returns false. This causes getAssetPrice to use the fallback oracle price instead of the primary source price, which is not the intended design.

According to the Chainlink "L2 Sequencer Uptime Feeds" documentation, correct integration with L2 sequencer uptime feeds requires 1) checking the sequencer status and reverting if it is down, and 2) implementing a grace period after the sequencer restarts.

Impact

The incorrect freshness check from the sequencer feed causes getAssetPrice to use the fallback oracle price when it should use the primary source price. If the fallback oracle is unset, getAssetPrice returns 0, which causes reverts in calling functions.

Additionally, the missing sequencer status check allows getAssetPrice to use stale prices. When the L2 sequencer goes down, price oracles stop updating data. Stale prices can appear fresh during sequencer downtime.

Recommendations

We recommend removing the incorrect _isSequencerOracleFresh check and implementing the L2 sequencer uptime check specified by Chainlink.

Remediation

This issue has been acknowledged by Blueprint Finance, and a fix was implemented in commit 731f01d7.

Zellic © 2025Back to top ↑