Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Valid

Incorrect Timestamp Usage in `StreamsLookup Revert`

Summary

checkLog function in UsdTokenSwapKeeper contract uses block.timestamp (current time) instead of log.timestamp (event emission time) to query historical price data, leading to incorrect swap executions that violate user expectations and protocol integrity.

Vulnerability Details

When a user submits a swap request via MarketMakingEngine, an event is emitted with a specific timestamp (stored in log.timestamp). Chainlink Automation Network detects this event and calls checkLog on UsdTokenSwapKeeper.The checkLog function is designed to fetch the price at the time of the event using Chainlink Data Streams. However, the code incorrectly uses block.timestamp (the current block time) to query the price feed:
UsdTokenSwapKeeper.sol#L117

function checkLog(
AutomationLog calldata log,
bytes memory
)
external
view
returns (bool upkeepNeeded, bytes memory performData)
{
string[] memory streams = new string[](1);
streams[0] = self.streamId;
// encode perform data
bytes memory extraData = abi.encode(caller, requestId);
revert StreamsLookup(
DATA_STREAMS_FEED_LABEL,
streams,
DATA_STREAMS_QUERY_LABEL,
@> block.timestamp, // BUG: Uses current time instead of event time
extraData
);
}

The log.timestamp is the time when the log was emitted, which corresponds to the block time when the event happened. On the other hand, block.timestamp is the current block's timestamp when the checkLog function is executed. These might not be the same if there's a delay between the event emission and the checkLog call.

In the context of fulfilling a swap request, the price data needed is the one at the time the request was made (log.timestamp), not the current time. If block.timestamp is used, the price might have changed, leading to incorrect fulfillment. This could cause issues like using outdated or incorrect prices, affecting the swap's execution.

Chainlink's documentation example itself use log.timestamp for the Streams lookup in checkLog:

// This function uses revert to convey call information.
// See https://eips.ethereum.org/EIPS/eip-3668#rationale for details.
function checkLog(
Log calldata log,
bytes memory
) external returns (bool upkeepNeeded, bytes memory performData) {
revert StreamsLookup(
DATASTREAMS_FEEDLABEL,
feedIds,
DATASTREAMS_QUERYLABEL,
@> log.timestamp,
""
);
}

Impact

Using an incorrect timestamp could result in fetching price data from a different time than when the swap was actually requested, potentially leading to unfair pricing.

Tools Used

Manual Review

Recommendations

The correct approach should be to use log.timestamp to ensure the data corresponds to the event's time:

revert StreamsLookup(
DATA_STREAMS_FEED_LABEL,
streams,
DATA_STREAMS_QUERY_LABEL,
- block.timestamp,
+ log.timestamp
extraData
);
Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

StreamsLookup revert should use log.timestamp instead of block.timestamp

Appeal created

0xshoonya Submitter
5 months ago
inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

StreamsLookup revert should use log.timestamp instead of block.timestamp

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.