Does giving approval to a marketplace allow the marketplace contract deployer to move assets if they so wished?
1 Answer
Let's check a standard implementation by OpenZeppelin:
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
This is taken from here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L175
This line
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
uses the function "_isApprovedOrOwner" with the msg.sender and the tokenId. Let's have a look at this function:
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
Here, the function checks with a function "isApprovedForAll". Let's check that as well:
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
That function uses a mapping "_operatorApprovals" which is defined as:
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
This means, that an owner can approve an operator to move any token in ownership by the owner.
Let's have a final look at the famous "setApprovalForAll":
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
There it is:
_operatorApprovals[owner][operator] = approved;
This is the part, where the mapping between the owner and the operator is set to "true". Which means that the function call to "isApprovedOrOwner" will return true, allowing that caller to transfer any token of the owner.