Here's the relevant code:
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param _spender address of the spender to query
* @param _tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function isApprovedOrOwner(
address _spender,
uint256 _tokenId
)
internal
view
returns (bool)
{
address owner = ownerOf(_tokenId);
// Disable solium check because of
// https://github.com/duaraghav8/Solium/issues/175
// solium-disable-next-line operator-whitespace
return (
_spender == owner ||
getApproved(_tokenId) == _spender ||
isApprovedForAll(owner, _spender)
);
}
First check is whether the person wanting to spend the token is its owner (from the call to ownerOf()
):
_spender == owner ||
Relevant mapping is:
// Mapping from token ID to owner
mapping (uint256 => address) internal tokenOwner;
If not, we jump to the next check, which is whether the address of the spender has been approved by the owner to have control over that particular token ID. (So here the delegation is by reference to the token. i.e. The spender has permission for this particular token.)
getApproved(_tokenId) == _spender ||
Relevant mapping is:
// Mapping from token ID to approved address
mapping (uint256 => address) internal tokenApprovals;
If not, and even if the spender isn't the owner or has the single token approval, then they might be in the approved operator list. (In this case the delegation is via reference to the token's owner. i.e. The spender is allowed to "operate" in place of the owner, over any tokens they own.)
isApprovedForAll(owner, _spender)
Mapping:
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) internal operatorApprovals;