ERC721FloorBidMatcher blockchain reorg vulnerability#
Low Risk
A blockchain reorganization occurs when accepted blocks of transactions are thrown out and replaced with other blocks of transactions when a longer chain of truth is found. More information blockchain reorgs here: https://medium.com/blockvigil/how-we-deal-with-chain-reorganization-at-ethvigil-5a8c06859c7
Blockchain reorgs can cause vulnerabilities in smart contracts (that don't account for it) when users or software see and act on information in a blockchain that is later thrown out because of a blockchain reorganization.
A blockchain reorg vulnerability exists in ERC721FloorBidMatcher
due to the following circumstances:
- The
createBuyOrder
functions create order IDs sequentially. - The
matchBuyOrder
function usesuint256 orderId
to identify orders.
Here is what can happen:
- User A (or some bot or other software) sees a new order of NFTs from address X at the price of 10 USDC. The order ID is 112.
- User A submits a transaction to execute the
matchBuyOrder
function using order ID 112 to sell his NFTs at that price (10 USDC). - While User A's transaction is pending a block reorganization occurs that throws out the transaction that created order 112 and replaces it with a different transaction that creates a different order that is also order 112 (because order ids are sequentially created). This different order has a different price, let's say the price is 1 USDC instead of 10 USDC.
- User A's transaction executes the new/different order 112 and so the user sells his NFTs at a different price than he/she meant to.
Recommendation#
A way to prevent this is to generate the order id from the important information of the order plus an incrementing value like block.number
. For example order ids could be generated like this:
uint256 orderId = keccak256(abi.encodePacked(_msgSender(), erc721TokenAddress, paymentTokenAddress, tokenPrice, block.number))
Another way to prevent this is by using additional parameters in the matchBuyOrder
function to identify the order. For example matchBuyOrder
could have these parameters:
function matchBuyOrder(
uint256 orderId,
uint256[] calldata tokenIds,
address erc721TokenAddress,
address paymentTokenAddress,
uint256 tokenPrice
)
Note: While I have seen this problem actually occur on Polygon I have not seen it occur on Ethereum. So I don't know how likely this issue could happen. One thing that can be looked at is how often chain reorgs happen. Etherscan shows chain reorg info here: https://etherscan.io/blocks_forked