Got caught up playing with memory and calldata because i tried to call safeTransferFrom()
with the data argument from the safeTransferFrom()
whilst adding an empty string.
After 45 minutes, realised I could just call _checkERC721Support()
with the empty string.
I is idiot
In any case, my Safe Transfer Functions were:
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external override {
transferFrom(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, _data));
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external override {
transferFrom(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, ""));
}
function _safeTransfer(address _from, address _to, uint _tokenId, bytes memory _data) internal {
_transfer(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, _data));
}
function _checkERC721Support(address _from, address _to, uint _tokenId, bytes memory _data) internal returns (bool) {
if(!_isContract(_to)) {
return true;
}
// Call onERC721Received in the _to contract
bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
// Check return Value
return returnData == MAGIC_ERC721_RECEIVED;
}
function _isContract(address _to) view internal returns (bool) {
uint32 size;
assembly{
size := extcodesize(_to)
}
return size > 0;
}
The code of the whole contract was:
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./Ownable.sol";
import "./IERC721Receiver.sol";
contract Kittycontract is IERC721, Ownable {
string private tokenName = "KittyToken";
string private tokenSymbol = "KITTY";
uint constant MAXGENZEROCATS = 2000;
uint currentGenZeroCats;
bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
event Birth(address _owner, uint _kittenId, uint _mumId, uint _dadId, uint _genes);
struct Kitty {
uint16 genes;
uint64 birthTime;
uint32 mumId;
uint32 dadId;
uint16 generation;
}
function createKittyGenZero(uint _genes) external returns (uint kittyId) {
require(currentGenZeroCats < MAXGENZEROCATS);
currentGenZeroCats += 1;
return _createKitty(0, 0, 0, _genes, msg.sender);
}
function _createKitty(uint _mumId, uint _dadId, uint _generation, uint _genes, address _owner) private returns (uint kittyId) {
Kitty memory _kitty = Kitty({genes: uint16(_genes), birthTime: uint64(block.timestamp), mumId: uint32(_mumId), dadId: uint32(_dadId), generation: uint16(_generation)});
kitties.push(_kitty);
uint newKittenId = kitties.length - 1;
_transfer(address(0), _owner, newKittenId);
emit Birth(_owner, newKittenId, _mumId, _dadId, _genes);
return newKittenId;
}
function getKitty(uint256 _kittyId) external view returns (uint _genes, uint _birthTime, uint _mumId, uint _dadId, uint _generation, address _owner) {
require(_kittyId < kitties.length);
Kitty storage kitty = kitties[_kittyId];
_genes = uint256(kitty.genes);
_birthTime = uint256(kitty.birthTime);
_mumId = uint256(kitty.mumId);
_dadId = uint256(kitty.dadId);
_generation = uint256(kitty.generation);
_owner = ownerOf(_kittyId);
}
Kitty[] kitties;
mapping(uint => address) public kittyIndexToOwner;
mapping (address => uint) ownershipTokenCount;
mapping(uint => address) kittyIndexToApproved;
mapping (address => mapping (address => bool)) private _operatorApprovals;
function balanceOf(address owner) external override view returns (uint256 balance) {
return ownershipTokenCount[owner];
}
function totalSupply() external override view returns (uint256 total) {
return kitties.length;
}
function name() public view virtual override returns (string memory) {
return tokenName;
}
function symbol() public view virtual override returns (string memory) {
return tokenSymbol;
}
function ownerOf(uint256 tokenId) public override view returns (address owner) {
require(tokenId <= kitties.length, "Token Does Not Exist");
return kittyIndexToOwner[tokenId];
}
function transfer(address to, uint256 tokenId) external override {
require(to != address(0));
require(to != address(this));
require(kittyIndexToOwner[tokenId] == msg.sender);
_transfer(msg.sender, to, tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal {
if(from != address(0)) {
ownershipTokenCount[msg.sender] -= 1;
delete kittyIndexToApproved[tokenId];
}
ownershipTokenCount[to] += 1;
kittyIndexToOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function approve(address _to, uint256 _tokenId) external override {
require(kittyIndexToOwner[_tokenId] == msg.sender);
_approve(_to, _tokenId);
emit Approval(msg.sender, _to, _tokenId);
}
function _approve(address _to, uint256 _tokenId) internal {
kittyIndexToApproved[_tokenId] = _to;
}
function setApprovalForAll(address _operator, bool _approved) external override {
require (_operator != msg.sender);
_setApprovalForAll(_operator, _approved);
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function _setApprovalForAll(address _operator, bool _approved) internal {
_operatorApprovals[msg.sender][_operator] = _approved;
}
function getApproved(uint256 _tokenId) external override view returns (address) {
require (_tokenId < kitties.length); // Token Must Exist
return kittyIndexToApproved[_tokenId];
}
function isApprovedForAll(address _owner, address _operator) public override view returns (bool) {
return _operatorApprovals[_owner][_operator];
}
function transferFrom(address _from, address _to, uint256 _tokenId) public override {
if(_from != msg.sender && ownerOf(_tokenId) != msg.sender) {
require(_approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender) == true);
}
require(_tokenId < kitties.length);
require(_to != address(0));
_transfer(_from, _to, _tokenId);
}
function _approvedFor(address _claimant, uint _tokenId) internal view returns (bool) {
return kittyIndexToApproved[_tokenId] == _claimant;
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external override {
transferFrom(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, _data));
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external override {
transferFrom(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, ""));
}
function _safeTransfer(address _from, address _to, uint _tokenId, bytes memory _data) internal {
_transfer(_from, _to, _tokenId);
require(_checkERC721Support(_from, _to, _tokenId, _data));
}
function _checkERC721Support(address _from, address _to, uint _tokenId, bytes memory _data) internal returns (bool) {
if(!_isContract(_to)) {
return true;
}
// Call onERC721Received in the _to contract
bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
// Check return Value
return returnData == MAGIC_ERC721_RECEIVED;
}
function _isContract(address _to) view internal returns (bool) {
uint32 size;
assembly{
size := extcodesize(_to)
}
return size > 0;
}
}