safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) is not overridden to check _ableToTransfer() in FighterFarm transfers.
Therefore it is possible to hold more than MAX_FIGHTERS_ALLOWED fighters and transfer staked fighters.
This may enable the user to initiate more than 10 battles per day for a fighter, and may DoS updateBattleRecord().
Proof of Concept
FighterFarm inherits from ERC721 which contains three transfer functions. Two of these are overridden to include a require(_ableToTransfer(tokenId, to));
prevents holding more than MAX_FIGHTERS_ALLOWED fighters and prevents transfers of staked fighters.
But the public safeTransferFrom() which includes the data parameter is not overridden and therefore void of these new restrictions.
It is therefore possible to transfer fighters such that the recipient holds more than MAX_FIGHTERS_ALLOWED fighters, and to transfer staked fighters.
Lines of code
https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L533-L535
Vulnerability details
Impact
safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
is not overridden to check_ableToTransfer()
in FighterFarm transfers. Therefore it is possible to hold more thanMAX_FIGHTERS_ALLOWED
fighters and transfer staked fighters.This may enable the user to initiate more than 10 battles per day for a fighter, and may DoS
updateBattleRecord()
.Proof of Concept
FighterFarm inherits from ERC721 which contains three transfer functions. Two of these are overridden to include a
require(_ableToTransfer(tokenId, to));
prevents holding more than
MAX_FIGHTERS_ALLOWED
fighters and prevents transfers of staked fighters. But the publicsafeTransferFrom()
which includes thedata
parameter is not overridden and therefore void of these new restrictions.It is therefore possible to transfer fighters such that the recipient holds more than
MAX_FIGHTERS_ALLOWED
fighters, and to transfer staked fighters.A consequence of this is that the new owner has a fresh voltage and battles can therefore be initiated without limit.
Since points are added to or deducted from
accumulatedPointsPerFighter
andaccumulatedPointsPerAddress
in parallel at every win or loss, transferring the fighter to a new address, for which thenaccumulatedPointsPerAddress
is0
(or just different)_addResultPoints()
will attempt to remove more points then possible, since the points are calculated based on thetokenId
rather than thefighterOwner
. This will then cause a DoS ofupdateBattleRecord()
due to underflow.Recommended Mitigation Steps
Override
_transfer()
,_beforeTokenTransfer()
or_afterTokenTransfer()
instead.Assessed type
DoS