When a token is transferred, it is not removed from the original owner's list of tokens (s_ownerToCatsTokenId[currCatOwner]
). Consequently, the internal ownership accounting will be incorrect.
When a token is transferred via KittyConnect::safeTransferFrom
, ownership info is supposed to be updated via a call to KittyConnect::_updateOwnershipInfo
:
However, KittyConnect::_updateOwnershipInfo
misses to remove the transferred token from the the original owner's list of tokens s_ownerToCatsTokenId[currCatOwner]
.
Onwership info (internal ownership) accounting will be incorrect and unreliable.
Note that this data can become even more entangled and cause more problems.
The idx
within the CatInfo
structure is used as an index to track the position of each NFT within an owner's array of token IDs (s_ownerToCatsTokenId[owner]
). This design aims to facilitate efficient management and lookup of NFTs owned by a particular user, especially for operations that involve modifying the ownership or characteristics of these NFTs.
idx
is relied on in KittyConnect::bridgeNftToAnotherChain
as follows:
However, after calling KittyConnect::safeTransferFrom
, idx
will be incorrect and unreliable. Consider the following scenario:
mintCatToNewOwner()
is called to mint an NFT to userA
. For this, Idx=0
, tokenId=0
.
safeTransferFrom()
is called to transfer tokenId=0
to userB
.
mintCatToNewOwner()
is called to mint a 2nd NFT to userA
. For this, Idx=1
, tokenId=1
(due to the bug).
mintCatToNewOwner()
is called to mint a 3rd NFT to userA
. For this, Idx=2
, tokenId=2
(due to the bug).
bridgeNftToAnotherChain()
is called by userA
to bridge tokenId=1
to another chain.
As a result, the s_ownerToCatsTokenId[userA]
array will incorrectly be [0, 1]
, while it really should be [2]
.
First Mint: userA receives an NFT (tokenId=0). The idx within CatInfo for this token is 0. The array s_ownerToCatsTokenId[userA] now contains [0].
Transfer to userB: tokenId=0 is transferred to userB. Due to the bug, s_ownerToCatsTokenId[userA] mistakenly still contains [0], although it should be empty.
Second Mint: Another NFT is minted to userA (tokenId=1). Given the bug, idx is incorrectly set to 1 for this new token because s_ownerToCatsTokenId[userA] was not updated properly earlier and still shows one token. After minting, s_ownerToCatsTokenId[userA] incorrectly shows [0, 1].
Third Mint: A third NFT (tokenId=2) is minted to userA. The idx for this token is set to 2, following the same incorrect pattern. Now, s_ownerToCatsTokenId[userA] is [0, 1, 2], which is incorrect because tokenId=0 should not be there.
Bridging tokenId=1: When bridgeNftToAnotherChain() is called for tokenId=1 by userA, the function attempts to remove tokenId=1 from userA's list. Here's what happens, step by step::
delete s_catInfo[tokenId]; removes the CatInfo for tokenId=1.
It then attempts to remove tokenId=1 from userA's list of token IDs by popping the last item and replacing tokenId=1's position if it's not the last item.
Given the array state [0, 1, 2]:
The last item (2) is stored in lastItem.
The last item is popped, so before any replacement, the array temporarily looks like [0, 1].
Since idx for tokenId=1 is 1, and it's not less than the current last index (1), no swap occurs.
Values left in s_ownerToCatsTokenId[userA]:
The array will be [0, 1] immediately after popping 2, and since the removal logic does not correctly address the middle of the array without additional steps, it incorrectly remains [0, 1] (assuming no swap due to incorrect indexing logic). The intended outcome after correctly removing tokenId=1 should have been just [0] considering the original bug, but with proper management, it should actually end up being [2] after the transfer bug is fixed and transfers are properly managed.
Manual review, Foundry.
Modify the _updateOwnershipInfo
function to include the removal of thettokenId
from the currCatOwner
's list.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.