Frontrunning Vulnerability in Token Reservation Process
Summary
The reserve function in the Listings contract is susceptible to frontrunning attacks, where an attacker can observe and preemptively execute transactions to reserve tokens before the original user. This vulnerability can lead to unauthorized reservations and disrupt the intended reservation process.
Vulnerability Detail
Frontrunning occurs when an attacker monitors pending transactions in the blockchain's public mempool and submits their own transaction with a higher gas fee to be processed first. In the context of the reserve function, this allows an attacker to become the reserver of a token by front-running legitimate users' transactions.
Impact
Unauthorized Reservations: Attackers can reserve tokens intended for other users, potentially blocking legitimate access.
User Frustration: Legitimate users may experience failed transactions and increased costs as they attempt to reserve tokens.
Market Manipulation: Frontrunners can exploit the reservation process for personal gain, disrupting market dynamics.
Code Snippet
function reserve(address _collection, uint _tokenId, uint _collateral) public nonReentrant lockerNotPaused {
// Read the existing listing in a single read
Listing memory oldListing = _listings[_collection][_tokenId];
// Ensure the caller is not the owner of the listing
if (oldListing.owner == msg.sender) revert CallerIsAlreadyOwner();
// Ensure that the existing listing is available
(bool isAvailable, uint listingPrice) = getListingPrice(_collection, _tokenId);
if (!isAvailable) revert ListingNotAvailable();
// Find the underlying {CollectionToken} attached to our collection
ICollectionToken collectionToken = locker.collectionToken(_collection);
// Check if the listing is a floor item and process additional logic if there
// was an owner (meaning it was not floor, so liquid or dutch).
if (oldListing.owner != address(0)) {
// We can process a tax refund for the existing listing if it isn't a liquidation
if (!_isLiquidation[_collection][_tokenId]) {
(uint _fees,) = _resolveListingTax(oldListing, _collection, true);
if (_fees != 0) {
emit ListingFeeCaptured(_collection, _tokenId, _fees);
}
}
// If the floor multiple of the original listings is different, then this needs
// to be paid to the original owner of the listing.
uint listingFloorPrice = 1 ether * 10 ** collectionToken.denomination();
if (listingPrice > listingFloorPrice) {
unchecked {
collectionToken.transferFrom(msg.sender, oldListing.owner, listingPrice - listingFloorPrice);
}
}
// Reduce the amount of listings
unchecked { listingCount[_collection] -= 1; }
}
// Burn the tokens that the user provided as collateral, as we will have it minted
// from {ProtectedListings}.
collectionToken.burnFrom(msg.sender, _collateral * 10 ** collectionToken.denomination());
// We can now pull in the tokens from the Locker
locker.withdrawToken(_collection, _tokenId, address(this));
IERC721(_collection).approve(address(protectedListings), _tokenId);
// Create a protected listing, taking only the tokens
uint[] memory tokenIds = new uint[](1);
tokenIds[0] = _tokenId;
IProtectedListings.CreateListing[] memory createProtectedListing = new IProtectedListings.CreateListing[](1);
createProtectedListing[0] = IProtectedListings.CreateListing({
collection: _collection,
tokenIds: tokenIds,
listing: IProtectedListings.ProtectedListing({
owner: payable(address(this)),
tokenTaken: uint96(1 ether - _collateral),
checkpoint: 0 // Set in the `createListings` call
})
});
// Create our listing, receiving the ERC20 into this contract
protectedListings.createListings(createProtectedListing);
// We should now have received the non-collateral assets, which we will burn in
// addition to the amount that the user sent us.
collectionToken.burn((1 ether - _collateral) * 10 ** collectionToken.denomination());
// We can now transfer ownership of the listing to the user reserving it
protectedListings.transferOwnership(_collection, _tokenId, payable(msg.sender));
}
Commit-Reveal Scheme: Implement a two-step process where users first commit to reserving a token and later reveal their commitment, reducing the predictability of transactions.
Randomized Delays: Introduce a randomized delay or time window for reservations to make it harder for attackers to predict transaction timing.
Gas Price Management: Encourage users to set competitive gas prices and monitor network conditions to reduce the likelihood of being frontrun.
0xBhumii
Medium
Frontrunning Vulnerability in Token
Reservation
ProcessSummary
The
reserve
function in theListings
contract is susceptible to frontrunning attacks, where an attacker can observe and preemptively execute transactions to reserve tokens before the original user. This vulnerability can lead to unauthorized reservations and disrupt the intended reservation process.Vulnerability Detail
Frontrunning occurs when an attacker monitors pending transactions in the blockchain's public mempool and submits their own transaction with a higher gas fee to be processed first. In the context of the
reserve
function, this allows an attacker to become the reserver of a token by front-running legitimate users' transactions.Impact
Unauthorized Reservations: Attackers can reserve tokens intended for other users, potentially blocking legitimate access. User Frustration: Legitimate users may experience failed transactions and increased costs as they attempt to reserve tokens. Market Manipulation: Frontrunners can exploit the reservation process for personal gain, disrupting market dynamics.
Code Snippet
https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/Listings.sol#L690
Tool used
Manual Review
Recommendation
Commit-Reveal Scheme: Implement a two-step process where users first commit to reserving a token and later reveal their commitment, reducing the predictability of transactions.
Randomized Delays: Introduce a randomized delay or time window for reservations to make it harder for attackers to predict transaction timing.
Gas Price Management: Encourage users to set competitive gas prices and monitor network conditions to reduce the likelihood of being frontrun.