Assignment - Safetransfer Implementation

ok thanks and yes I assume that I just need a confirmation that the new version requires to have override specifier. As you can see I did add that to all function, but it is strange because I thought Inheritance is much older than compiler 5 and should throw error for Filip too.

Indeed, the solidity version could show some error messages that are related to a syntax issue, i also advice you to check your interfaces functions, maybe the statement is different.

The definition of the function in the contract should be the same than the interface, so maybe could be also why those errors:

Carlos Z

I’m not sure I’m understanding what you mean. Do you mean the name of the function, properties and the header should be the same? I understand that they should yes but this is why there should be override specifier, because they are the same. I understand that the error is on because those functions are the same and the public one should have override specifier to let know that this is the function that is overriding the other one.

safeTransfer and related functions >> it compiles but I have not run tests:

code

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) override external {
        require(to != address(0), "Cannot send to a zero address");
        require(to != address(this), "Cannot transfer to this contract");
        require(
            (kittyIndexToOwner[tokenId] == msg.sender) ||
            (kittyIndexToApproved[tokenId] == msg.sender),
            "Owner must own token or be an approved operator");
        require(from == msg.sender, "Must send from own account");
        require(_exists(tokenId), "Token does not exist");

        _safeTransfer(from, to, tokenId, data);

    }


    function safeTransferFrom(address from, address to, uint256 tokenId) override external {
        require(to != address(0), "Cannot send to a zero address");
        require(to != address(this), "Cannot transfer to this contract");
        require(
            (kittyIndexToOwner[tokenId] == msg.sender) ||
            (kittyIndexToApproved[tokenId] == msg.sender),
            "Owner must own token or be an approved operator");
        require(from == msg.sender, "Must send from own account");
        require(_exists(tokenId), "Token does not exist");

        _safeTransfer(from, to, tokenId, "");

    }


    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal {
        _transfer(from, to, tokenId);
        require( _checkOnERC721Support(from, to, tokenId, data));
    }


    function isContract(address to) view internal returns (bool){
        uint32 size;
        assembly{
            size := extcodesize(to)
        }
        return size > 0;
    }


    function _checkOnERC721Support(address from, address to, uint256 tokenId, bytes memory data) internal returns (bool){
        if(!isContract(to)){
            return true;
        }

        bytes4 returnData = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data);
        return returnData == MAGIC_ERC721_RECEIVED;

    }


    function onERC721Received(address from, address to, uint256 tokenId, bytes memory data) override external returns (bytes4) {
        return MAGIC_ERC721_RECEIVED;
    }
1 Like

hi guys, @kenn.eth I am struggling with understanding and creating catalogue too. I’m trying to back engineer final code that I got from Mauro from support team, but what I don’t understand at the moment is how it suppose to work? When I click on the menu catalogue and it takes me to catalogue page, than that site should show all the kitties I owe? and whoever open that page for that person will be different kitties, those that they owe? Is that the idea of the catalogue?

also I can see that there is no buttons so the page, catalogue, with owed kitties, should load automatically for each person different?

I was trying to deploy on my local server your final code, to see how it works, but when I tried do it with python and truffle develop I got that error:

C:\Users\kamil\Documents\Ethereum-201\academy-cryptokitties-dev>truffle develop
Error: Cannot find module '@truffle/hdwallet-provider'
Require stack:
- C:\Users\kamil\Documents\Ethereum-201\academy-cryptokitties-dev\truffle-config.js
- C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\node_modules\original-require\index.js
- C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\cli.bundled.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:941:15)
    at Function.Module._load (node:internal/modules/cjs/loader:774:27)
    at Module.require (node:internal/modules/cjs/loader:1013:19)
    at require (node:internal/modules/cjs/helpers:93:18)
    at Object.<anonymous> (C:\Users\kamil\Documents\Ethereum-201\academy-cryptokitties-dev\truffle-config.js:25:26)
    at Module._compile (node:internal/modules/cjs/loader:1109:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
    at Module.load (node:internal/modules/cjs/loader:989:32)
    at Function.Module._load (node:internal/modules/cjs/loader:829:14)
    at Module.require (node:internal/modules/cjs/loader:1013:19)
    at Object.require (node:internal/modules/cjs/helpers:93:18)
    at Function.load (C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\config\dist\index.js:160:1)
    at Function.detect (C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\config\dist\index.js:149:1)
    at Object.run (C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\develop.js:48:1)
    at Command.run (C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\command.js:147:1)
    at Object.586806 (C:\Users\kamil\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\cli.js:51:1)
Truffle v5.3.7 (core: 5.3.7)
Node v16.2.0

so than I commented out in truffle-config.js the truffle-hdwallet-provider stuff and deployed like without it like I do with my own version but when I went to the website, created couple of gen0 kitties and opened catalogue, there was nothing on that page, so not sure why?

please help me to understand.

Hey @Kamil37,
1/ You got it right for the catalogue. Since it’s based on the user address, it will display the cats from “whoever is connected”.
It really is a pita to reverse engineer everything though, but it works eventually since I’m almost done! You can grab a few other code from Assignment - Marketplace Frontend to check different implementations.
2/ The @truffle/hdwallet-provider package is required to deploy on testnet/mainnet. It shouldn’t be needed on a local server. In doubt, you can install it, but you’ll also need to configure your ETH node (infura) and to create a .secret file with your seed phrase (a secondary account is advised!).
No expert here, just trying to help :slight_smile:

thanks mate, any idea why catalogue not working while deploying on my local server? does that got to do with me not having @truffle/hdwallet-provider?

Hard to say. Have you checked truffle-config to make sure the development server is uncomment?

BTW, is this the final code you’re talking about: https://github.com/Ivan-on-Tech-Academy/academy-cryptokitties ?

If so, the truffle-config file is messed up. In this case, you should install the @truffle/hdwallet-provider package and use the network ganache on port 8545 instead of the default one development.
If not, I’m interested if you can share the link.

yes that’s the code, got same link. I want to avoid using ganache if I can, with truffle develop all seam to be much simpler and less bugs. I did deploy it with truffle develop but had to commented out @truffle/hdwallet-provider

pragma solidity 0.8.0;

// import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./Ownable.sol";

contract Kittycontract is Ownable {


    string public constant tickerName = "ThePowerOfMeow";
    string public constant tickerSymbol = "MEOW";
    uint256 totalTokenCount;

    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

    uint256 public constant CREATION_LIMIT_GEN0 = 10;
    uint256 public gen0Counter = 0;

    event Birth(
      address owner,
      uint256 kittenId,
      uint256 matronId,
      uint256 sireId,
      uint256 genes
    );

    struct Kitty {
        uint256 genes;
        uint64 birthTime;
        uint32 matronId;
        uint32 sireId;
        uint16 generation;
    }

    Kitty[] kitties;

    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    mapping(address => uint256) private ownershipTokenCount;
    mapping(uint256 => address) public ownedBy;

    mapping(uint256 => address) public kittyIndexToApproved; //address can transfer token on owner's behalf
    mapping(address => mapping(address => bool)) private _operatorApprovals; //address can transfer ALL tokens on owner's behalf

    function _createKitty(
      uint256 _matronId,
      uint256 _sireId,
      uint256 _generation,
      uint256 _genes,
      address _owner
    ) private returns(uint256) {
        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(block.timestamp),
            matronId:uint32(_matronId),
            sireId: uint32(_sireId),
            generation: uint16(_generation)
        });
        kitties.push(_kitty);
        uint256 newKittenId = kitties.length - 1;
        emit Birth(_owner, newKittenId, _matronId, _sireId, _genes);
        _transfer(address(0), _owner, newKittenId);
        return newKittenId;
    }

    function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256 id){
        require(gen0Counter < CREATION_LIMIT_GEN0, "Maximum Gen0 limit reached");
        gen0Counter++;
        return _createKitty(0, 0, 0, _genes, msg.sender);
    }

    function getKitty(uint kittenId) public view returns(Kitty memory) {
        return kitties[kittenId];
    }

    function balanceOf(address owner) external view returns(uint256 balance) {
        return ownershipTokenCount[owner];
    }

    function totalSupply() external view returns (uint256 total) {
        return kitties.length;
    }

    function name() external view returns (string memory tokenName) {
        return tickerName;
    }

    function symbol() external view returns (string memory tokenSymbol) {
        return tickerSymbol;
    }

    function ownerOf(uint256 tokenId) external view returns (address owner) {
        address tokenOwner = ownedBy[tokenId];
        require(tokenId < kitties.length, "Token does not exist");
        return tokenOwner;
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
        require(validateOperator(msg.sender, tokenId) || kittyIndexToApproved[tokenId] == msg.sender, "Unauthorized operator");
        require(to != address(0), "Invalid recipient, cannot transfer to zero addres");
        _safeTransfer(from, to, tokenId, data);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) external {
      require(validateOperator(msg.sender, tokenId) || kittyIndexToApproved[tokenId] == msg.sender, "Unauthorized operator");
      require(to != address(0), "Invalid recipient, cannot transfer to zero addres");
      _safeTransfer(from, to, tokenId, "");
    }

    function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
        _transfer(_from, _to, _tokenId);
        require(_checkIERC721Support(_from, _to, _tokenId, _data));
    }

    function transfer(address to, uint256 tokenId) external {
        require(to != address(0), "Invalid recipient, cannot transfer to zero addres");
        require(to != address(this), "Invalid recipient, cannot transfer to this contract");
        require(to != msg.sender, "Invalid recipient, you cannot transfer to yourself");
        require(ownedBy[tokenId] == msg.sender, "Transfer function is only for token owner, operator should use transferFrom");
        _transfer(msg.sender, to, tokenId);
    }

    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        ownershipTokenCount[_to]++;
        if(_from != address(0)){
            ownershipTokenCount[_from]--;
            delete kittyIndexToApproved[_tokenId];
        }
        ownedBy[_tokenId] = _to;
        emit Transfer(_from, _to, _tokenId);
    }

    function approve(address approved, uint256 tokenId) external {
        require(ownedBy[tokenId] != address(0));
        require(validateOperator(msg.sender, tokenId), "Only token owner or authorized operator can approve");
        _approve(ownedBy[tokenId], approved, tokenId);
    }

    function _approve(address _owner, address _approved, uint256 _tokenId) internal {
        kittyIndexToApproved[_tokenId] = _approved;
        emit Approval(_owner, _approved, _tokenId);
    }

    function setApprovalForAll(address operator, bool approved) external {
        require(operator != msg.sender, "Operator must be a third party");
        _setApprovalForAll(operator, approved);
    }

    function _setApprovalForAll(address _operator, bool _approved) internal {
        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    function getApproved(uint256 tokenId) public view returns (address) {
        require(tokenId < kitties.length, "Token does not exist");
        return kittyIndexToApproved[tokenId];
    }

    function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
        return _operatorApprovals[_owner][_operator];
    }

    function transferFrom(address from, address to, uint256 tokenId) external {
        require(validateOperator(msg.sender, tokenId) || kittyIndexToApproved[tokenId] == msg.sender, "Unauthorized operator");
        require(from == ownedBy[tokenId], "Can only transfer from token owner");
        require(to != address(0), "Token has no owner");
        require(tokenId < kitties.length, "Token does not exist");
        _transfer(from, to, tokenId);
    }

    function validateOperator(address _operator, uint _tokenId) internal view returns(bool) {
        bool validOperator = false;
        address tokenOwner = ownedBy[_tokenId];
        if(tokenOwner == _operator){
            validOperator = true;
        }
        else if(_operatorApprovals[tokenOwner][_operator] == true){
            validOperator = true;
        }
        return validOperator;
    }

    function _checkIERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns(bool) {
        if(!_isContract(_to)){ // if _to is NOT a contract, that means it's a wallet and all is well
            return true;
        }

        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
        return returnData == MAGIC_ERC721_RECEIVED;
    }

    function _isContract(address _to) internal view returns(bool) {
        //check if address _to code size is > 0, which means it's a contract rather than a wallet
        uint32 size;
        assembly{
            size := extcodesize(_to)
        }
        return size > 0;
    }

}

1 Like

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 :clown_face:

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;
    }
}
1 Like
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external {
        require((tokenOwner[_tokenId] == msg.sender) || tokenToApproved[_tokenId] == msg.sender || (operatorApprovals[tokenOwner[_tokenId]][msg.sender]), "Caller is not authorized");
        require(tokenOwner[_tokenId] == _from, "Giving address is not the owner");
        require(_to != address(0));
        require(_tokenId < kitties.length, "Token does not exist");

        _safeTransfer(_from, _to, _tokenId, data);      
    }

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        require((tokenOwner[_tokenId] == msg.sender) || tokenToApproved[_tokenId] == msg.sender || (operatorApprovals[tokenOwner[_tokenId]][msg.sender]), "Caller is not authorized");
        require(tokenOwner[_tokenId] == _from, "Giving address is not the owner");
        require(_to != address(0));
        require(_tokenId < kitties.length, "Token does not exist");
        
        _safeTransfer(_from, _to, _tokenId, "");
    }
3 Likes
safeTransfer functions + supplementary functions
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
        _transferRequireCheck(_from, _to, _tokenId);
        _safeTransfer(_from, _to, _tokenId, _data);
        emit fishyTransfer(_from, _to, _tokenId);
    }

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        bytes memory _data;
        _transferRequireCheck(_from, _to, _tokenId);
        _safeTransfer(_from, _to, _tokenId, _data);
        emit fishyTransfer(_from, _to, _tokenId);
    }
	
	    function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data ) internal {
        _transfer(_from, _to, _tokenId);
        require(_checkERC721Support(_from, _to, _tokenId, _data));
    }

    function _checkERC721Support(address _from, address _to, uint256 _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);
        return returnData == MAGIC_ERC721_RECEIVED;

        //Check return value

    }

    function _isContract(address _to) view internal returns (bool) {
        //code size > 0 --> contract
        //code size == 0 --> address
        uint32 size;
        assembly{
            size := extcodesize(_to)
        }
        return size > 0;
    }

    function _transferRequireCheck(address _from, address _to, uint256 _tokenId) internal view {
        //1. msg.sender == current owner/operator/approved
        //2. _from == current owner
        //3. _to address must exist
        //4. _tokenId must exist
        require(_owns(_from, _tokenId), "Ensure 'from' address owns the token"); //2
        require(_to != address(0), "Ensure address exists"); //3
        require(_tokenId < fishies.length, "Ensure token exists"); //4
        require((msg.sender == _from) || 
                (_addressApproved[_tokenId] == msg.sender) ||
                (_isApprovedForAll(_from, msg.sender)), 
                "Ensure user is either the owner, approved by owner, or a set operator"); //1
    }
2 Likes
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public {
    require(_to != address(0), "Reciever must not be dead address!");
    require(_isApprovedOrOwner(_from, _tokenId), "Must be owner or an approved operator to transfer the NFT!");

    _safeTransfer(_from, _to, _tokenId, _data);
  }
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
    safeTransferFrom(_from, _to, _tokenId, "");
}
3 Likes

Hi, this is my code:

...
bytes4 private constant MAGIC_VALUE =
        bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

...

Function: safeTransferFrom with data parameter

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes calldata data
    ) external payable transferFromCheck(_from, _to, _tokenId) {
        _safeTransfer(_from, _to, _tokenId, data);
    }

Function: safeTransferFrom without data parameter

    function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) external payable transferFromCheck(_from, _to, _tokenId) {
        _safeTransfer(_from, _to, _tokenId, "");
    }

Modifier: that checks all the requirements before transfering a token from an owner

modifier transferFromCheck(
        address _from,
        address _to,
        uint256 _tokenId
    ) {
        require(
            _to != address(0),
            "Kitties: Can't transfer token to address 0"
        );
        require(
            _owns(msg.sender, _tokenId) ||
                _isApprovedForThis(msg.sender, _tokenId) ||
                isApprovedForAll(_from, msg.sender),
            "Kitties: You are not authorized to transfer this token"
        );
        require(
            _owns(_from, _tokenId),
            "Kitties: _from is not the owner of the token"
        );
        require(_tokenId < kitties.length, "Kitties: Token id is not valid");

        _;
    }

The other helper functions

    function _safeTransfer(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes memory data
    ) internal {
        _transfer(_from, _to, _tokenId);
        require(_checkERC721Support(msg.sender, _from, _to, _tokenId, data));
    }

    function _checkERC721Support(
        address _operator,
        address _from,
        address _to,
        uint256 _tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (!_isContract(_to)) {
            return true;
        }
        bytes4 returnedValue = ERC721TokenReceiver(_to).onERC721Received(
            _operator,
            _from,
            _tokenId,
            _data
        );

        return returnedValue == MAGIC_VALUE;
    }

    function _isContract(address _to) private view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(_to)
        }
        return size > 0;
    }

    function _transfer(
        address from_,
        address to_,
        uint256 tokenId_
    ) internal {
        ownershipTokenCount[to_]++;

        if (from_ != address(0)) {
            ownershipTokenCount[from_]--;
            delete kittyIndexToApproved[tokenId_];
        }

        kittyIndexToOwner[tokenId_] = to_;
        emit Transfer(from_, to_, tokenId_);
    }

    function _owns(address claimant_, uint256 tokenId_)
        internal
        view
        returns (bool)
    {
        return kittyIndexToOwner[tokenId_] == claimant_;
    }
2 Likes

My code: Kittycontract.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.12;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./Ownable.sol";

contract Kittycontract is IERC721, Ownable {
  string public constant Name = "DemoKitties";
  string public constant Symbol = "DK";
  uint256 public constant CREATE_LIMIT_GEN0 = 10;
  
  bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

  event Birth(address owner, uint256 kittenId, uint256 mumId, uint256 dadId, uint256 genes, uint256 generation, uint256 birthTime);

  struct Kitty {
    uint256 genes;
    uint64 birthTime;
    uint32 mumId;
    uint32 dadId;
    uint16 generation;
  }

  Kitty[] private allKitties; // index => kitty id (token id)

  mapping (address => uint256) ownerTokenBalance; // get uint256 amount (token kitty count) of owner address
  mapping (uint256 => address) public kittyIndexToOwner; // get owner address of uint256 kitty id (token id)
  mapping (uint256 => address) public kittyIndexToApproved; // address which is approved (to tx it) for a kitty id

  // MYADDRESS => OPERATORADDRESS => True|False : give an addess tx rights of all my Kitties 
  // Ex.: _operatorApprovals[MYADDRESS][OPERATORADDRESS] -> True|False 
  mapping (address => mapping (address => bool)) private _operatorApprovals; 

  uint256 public gen0Counter;

  // ==============

  function createKittenGen0(uint256 _genes) public onlyOwner {
    require(gen0Counter < CREATE_LIMIT_GEN0, "Max number of Generation0 kittens reached");
    gen0Counter++;

    _createKitty(0, 0, 0, _genes, msg.sender); // no parents, owned by the contract
  }

  function _createKitty(
    uint256 _mumId, uint256 _dadId,
    uint256 _generation, uint256 _genes,
    address _owner
  ) private returns (uint256) { // cat Id
    uint64 _now = uint64(now);
    Kitty memory _kitty = Kitty({
      genes: _genes, birthTime: _now,
      mumId: uint32(_mumId), dadId: uint32(_dadId),
      generation: uint16(_generation)
    });

    uint256 newKittenId = allKitties.push(_kitty) -1; // zero based Id

    emit Birth(_owner, newKittenId, _mumId, _dadId, _genes, _generation, uint256(_now));

    _transfer(address(0), _owner, newKittenId); // address(0): birth of new kitten to owner

    return newKittenId;
  }

  function getKitty(uint256 _id) external view returns(
    uint256 genes, uint256 birthTime,
    uint256 mumId, uint256 dadId,
    uint256 generation
  ) {
    Kitty storage kitty = allKitties[_id]; // storage is a pointer to the original mapping, memory creates a local copy

    genes = kitty.genes;
    birthTime = uint256(kitty.birthTime);
    mumId = uint256(kitty.mumId);
    dadId = uint256(kitty.dadId);
    generation = uint256(kitty.generation);
  }

  // =================

  function balanceOf(address _owner) external view returns (uint256) {
    return ownerTokenBalance[_owner];
  }

  function totalSupply() public view returns (uint256) {
    return allKitties.length;
  }

  function name() public view returns (string memory) {
    return Name;
  }

  function symbol() public view returns (string memory tokenSymbol) {  
    return Symbol;
  }

  function ownerOf(uint256 _tokenId) external view returns (address) {
    address owner = kittyIndexToOwner[_tokenId];
    require(owner != address(0), "No owner of not existing address");
    return owner;
  }

  function transfer(address _to, uint256 _tokenId) external {
    require(_to != address(0), "TO address must be defined.");
    require(_to != address(this), "Cannot transfer to the contract itself");
    require(_to != msg.sender, "Cannot send to yourselves");
    require(_owns(msg.sender, _tokenId), "Cannot send token you not own");

    _transfer(msg.sender, _to, _tokenId);
  }

  function _transfer(address _from, address _to, uint256 _tokenId) internal {
    ownerTokenBalance[_to]++;
    if (_from != address(0)) {
      ownerTokenBalance[_from]--;
      delete kittyIndexToApproved[_tokenId]; // also delete current approved addresses when transferred
    }
    kittyIndexToOwner[_tokenId] = _to;

    emit Transfer(_from, _to, _tokenId);
  }

  function _owns(address claimant, uint256 tokenId) internal view returns (bool) {
    return kittyIndexToOwner[tokenId] == claimant;
  }

  // ---- done for 2nd IERC721.sol.

  function approve(address _toApprove, uint256 _tokenId) external {
    require(_owns(msg.sender, _tokenId), "Sender must be owner of token Id");
    require( ! _owns(_toApprove, _tokenId), "Address already is approved");

    _approve(_tokenId, _toApprove);
    emit Approval(msg.sender, _toApprove, _tokenId);
  }
  
  function setApprovalForAll(address _toAddOperator, bool _approved) external {
    require(_toAddOperator != msg.sender, "Cannot add myself (owner) again");

    _operatorApprovals[msg.sender][_toAddOperator] = _approved;
    emit ApprovalForAll(msg.sender, _toAddOperator, _approved);
  }
  
  function getApproved(uint256 _tokenId) external view returns (address) {
    require(_tokenId < totalSupply(), "token Id must exist");

    return kittyIndexToApproved[_tokenId]; // address(0) if no kitties
  }
  
  function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
    return _operatorApprovals[_owner][_operator]; // true if _operator is approved for _owner
  }

  function _approve(uint256 _tokenId, address _approved) internal {
    kittyIndexToApproved[_tokenId] = _approved;
  }

  // done for 3th IERC721.sol.

  function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
    safeTransferFrom(_from, _to, _tokenId, "");
  }

  function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public {
    require( _isApprovedOrOwner(msg.sender, _from, _to, _tokenId), "Must be approved or the owner of token Id");
    _safeTransfer(_from, _to, _tokenId, _data);
  }

  function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
    _transfer(_from, _to, _tokenId);
    require( _checkERC721Support(_from, _to, _tokenId, _data) );
  }

  function transferFrom(address _from, address _to, uint256 _tokenId) external {
    require( _isApprovedOrOwner(msg.sender, _from, _to, _tokenId), "Must be approved or the owner of token Id");
    /* replaces:
    require(_owns(_from, _tokenId), "Must be owner the token Id");
    require(_owns(msg.sender, _tokenId)
          || _approvedFor(msg.sender, _tokenId)
          || isApprovedForAll( _from, msg.sender),
          "Must be owner, be operator or approved of the token Id");
    require(_to != address(0), "Cannot transfer to zero address");
    require(_tokenId < totalSupply(), "token Id must exist");
    */

    _transfer(_from, _to, _tokenId);
  }

  function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
    return kittyIndexToApproved[_tokenId] == _claimant; // is claimant approved for token Id?
  }

  function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
    if( !_isContract(_to) ) {
      return true;
    }

    // call onERC721Received in _to contract and check returned value
    bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
    return returnData == MAGIC_ERC721_RECEIVED;
  }

  function _isContract(address _to) internal view returns (bool) {
    uint32 size;
    assembly{
      size := extcodesize(_to) // get _to address contract size
    }
    return size > 0;
  }

  function _isApprovedOrOwner(address _spender, address _from, address _to, uint256 _tokenId) internal view returns (bool) {
    require(_owns(_from, _tokenId), "Must be owner the token Id");
    require(_to != address(0), "Cannot transfer to zero address");
    require(_tokenId < totalSupply(), "token Id must exist");

    // spender is from || spender is approved for token Id || spender is operator for from
    return (_spender == _from
          || _approvedFor(_spender, _tokenId)
          || isApprovedForAll( _from, _spender) );
  }
}

IERC721Receiver.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.12;

interface IERC721Receiver {
  function onERC721Received(address operator, address from, uint tokenId, bytes calldata data) external returns (bytes4);
}
2 Likes

CatContract.sol:

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Reciever.sol";
import "./Ownable.sol";

abstract contract CatContract is IERC721, IERC721Reciever, Ownable {

    uint256 public constant CREATION_LIMIT_GEN0 = 5; //this provides us scarcity for the gen0 cats, we don't want to just give everyone the ability to create gen0 cats willy nilly.
    string public constant _name = "ZKitties";
    string public constant _symbol = "ZK";

    bytes4 internal constant MAGIC_ERC721_RECIEVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); //in EIP721

    /* event Transfer(address indexed from, address indexed to, uint256 indexed _tokenId);
    *
    * event Approval(address indexed owner, address indexed _approved, uint256 indexed _tokenId);
    *
    *event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    */
    event Birth(
        address owner,
        uint256 kittenId, 
        uint256 momId, 
        uint256 dadId, 
        uint256 genes
    );
    
    struct Kitty {
        uint256 genes;
        uint64 birthTime;
        uint32 fatherID;
        uint32 motherID;
        uint16 generation;
    }

    Kitty [] kitties; //array

    mapping(uint256 => address) public kittyIndexToOwner;
    mapping(address => uint256) ownerTokenCount;

    mapping(uint256 => address) public kittyIndexToApproved; //kitty id points to address that has been granted permission to transfer kitty id somewhere else on behalf of kitty id owner
    //getApproval and setApproval

    //MYADDRESS => OPPERATORADDRESS => TRUE/FALSE
    mapping(address => mapping(address => bool)) private _operatorApprovals;
    //setApprovalForAll and getApprovalForAll functions

    uint256 public gen0Counter;

    function createKittyGen0(uint256 _genes) public onlyOwner returns (uint256) {
        require(gen0Counter < CREATION_LIMIT_GEN0);

        gen0Counter++;
        
        return _createKitty(0,0,0, _genes, msg.sender);
    }

    function _createKitty(
        uint256 _momId,
        uint256 _dadId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    ) private returns (uint256) {
        Kitty memory _kitty = Kitty({
            genes: uint256(_genes),
            birthTime: uint64(block.timestamp),
            motherID: uint32(_momId),
            fatherID: uint32(_dadId),
            generation: uint16(_generation)
        });
        kitties.push(_kitty); //adds an element to the array, will return the size of the array

        uint256 newKittenId = kitties.length - 1; //Will continue to count upwards from 0 being the first in the array.

        emit Birth(_owner, newKittenId, _momId, _dadId, _genes); //new birth event

        _transfer(address(0), _owner, newKittenId); //the new kitten will be transferred to the owner from address 0.  This is the birth of the cat

        return newKittenId;

    }

    function getKitty(uint256 id) external view returns (
        uint256 birthTime,
        uint256 motherID,
        uint256 fatherID,
        uint256 generation,
        uint256 genes
        ){
        Kitty storage kitty = kitties[id]; //save as a pointer

        birthTime = uint256(kitty.birthTime);
        motherID = uint256(kitty.motherID);
        fatherID = uint256(kitty.fatherID);
        generation = uint256(kitty.generation);
        genes = kitty.genes;
    }
    

    function balanceOf(address owner) external override view returns (uint256 balance){
        return ownerTokenCount[owner];
    }

    function totalSupply() external override view returns (uint256 total) {
        return kitties.length; //length of the array of cats
    }

    
    function name() external override pure returns (string memory) {
        return _name;
    }

    function symbol() external override pure returns (string memory) {
        return _symbol;
    }
 

    function ownerOf(uint256 _tokenId) external override view returns (address) { //external functions tend to be cheaper gas
        return kittyIndexToOwner[_tokenId];
    }

    function transfer(address _to, uint256 _tokenId) external override { //only sends from msg.sender to a recipient
        require(_to != address(0), "Cannot send to this address");
        require(_to != address(this), "Cannot send to this contract");
        require(_owns(msg.sender,_tokenId), "Owner is msg.sender");

        emit Transfer(msg.sender, _to, _tokenId);
    }

    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        ownerTokenCount[_to]++; //increase token count of the recipient

        kittyIndexToOwner[_tokenId] = _to; //set ownership of kittyIndexToOwner so that the owner of _tokenId is _to

        if(_from != address(0)) {
            ownerTokenCount[_from]--; //if the from address isn't the 0 address we subtract the senders token count
            delete kittyIndexToApproved[_tokenId];
        }
        //emit the transfer event
        emit Transfer(_from, _to, _tokenId); //ERC721 Standard

    }

    function _owns(address _claimant, uint256 _tokenId) internal view returns(bool) {
        return kittyIndexToOwner[_tokenId] == _claimant;
    }
    function _approvedFor(address _claimant, uint256 _tokenId) internal view returns(bool) {
        return kittyIndexToApproved[_tokenId] == _claimant;
    }

    /*function _approve(uint256 _tokenId, address _approved) internal {
        return kittyIndexToApproved[_tokenId] = _approved;
    }
    */
    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external override { //one address will be able to approve for a tokenId
        require(_owns(msg.sender, _tokenId), "Msg.sender owns the tokenId");
        
        kittyIndexToApproved[_tokenId] = _approved;
        //_approve(_tokenId, _to); include if implementing the _approve function.

        emit Approval(msg.sender, _approved, _tokenId);
    }
    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external override {
        require(msg.sender != _operator);
        
        _operatorApprovals[msg.sender][_operator] = _approved;

        emit ApprovalForAll(owner, _operator, _approved);
    }

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId, address _approved) external view returns (address) {
       require(_tokenId > kitties.length);
       kittyIndexToApproved[_tokenId];
       return _approved;
    }

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) public view override returns (bool) {
        return _operatorApprovals[_owner][_operator];
    }
    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external override {
        require(msg.sender == _from || _approvedFor(msg.sender,_tokenId) ||  isApprovedForAll(_from, msg.sender), "msg.sender is current owner, or the authorized operator, or the approved address for this NFT");
        require(_owns(_from,_tokenId), "_from is not the current owner");
        require(_to != address(0), "_to is the zero address");
        require(_tokenId > kitties.length, "_tokenId is not a valid NFT");
        //Implement transfer function
        _transfer(_from,_to,_tokenId);
    }
    
    function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
        _transfer(_from,_to,_tokenId);
        require(_checkERC721Support(_from, _to, _tokenId, _data) ); //_checkERC721Support function needs to return as true, if it does not - it should throw an error
    }
    function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
        if( !_isContract(_to) ) {
            return true;
        }
        //Call _onERC721Recieved in the _to contract
        bytes4 returnData = IERC721Reciever(_from).onERC721Recieved(msg.sender, _from, _tokenId, _data);
        //Check return value
        return returnData == MAGIC_ERC721_RECIEVED;
    }
    function _isContract(address _to) internal view returns (bool) {
        uint32 size;
        assembly {
            size := extcodesize(_to)
        }
        return size > 0;
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external {
        require(msg.sender == _from || _approvedFor(msg.sender,_tokenId) ||  isApprovedForAll(_from, msg.sender), "msg.sender is current owner, or the authorized operator, or the approved address for this NFT");
        require(_owns(_from,_tokenId), "_from is not the current owner");
        require(_to != address(0), "_to is the zero address");
        require(_tokenId > kitties.length, "_tokenId is not a valid NFT");

        _safeTransfer(_from, _to, _tokenId, data);
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        _safeTransfer(_from, _to, _tokenId, "");
    }
}

IERCReciever.sol:

pragma solidity ^0.8.0;

interface IERC721Reciever {
    function onERC721Recieved(address operator, address from, uint tokenId, bytes calldata data) external returns (bytes4);
}
2 Likes

Kittycontract.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

import "./IERC721.sol";
import "./IERC721Receiver";
import "./Ownable.sol";

contract Kittycontract is IERC721, Ownable {

    uint256 public constant CREATION_LIMIT_GEN0 = 10;
    string public constant name = "KittyCripto";
    string public constant symbol = "KC";

    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

    event Birth(
    address owner, 
    uint256 kittenId,
    uint256 mumId, 
    uint256 dadId, 
    uint256 genes
    );
}

    struct Kitty {
        uint256 genes;
        uint64 birthTime;
        uint32 mumId;
        uint32 dadId;
        uint16 generation;
    }

   //Arrays of token Ids
   Kitty[] kitties;

    mapping (uint256 => address) public kittyIndexToOwner;
    mapping (address => uint256) ownershipTokenCount;

    mapping (uint256 => address) public kittyIndexApproved;
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    uint256 public gen0Counter;

    function _safetransfer (address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
        _transfer(_from, _to, _tokenId);
        require(_checkERC721Support(_from, _to, _tokenId, _data));

    } 

    function transferFrom(address _from, address _to, uint256 _tokenId) public {
        require(_to != address(0));
        require(msg.sender == _from || _approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender));
        require(_owns(_from, _tokenId));
        require(tokenId < Kitties.length);

        _transfer(_from, _to, _tokenId);

    }

    function approve(address _to, uint256 _tokenId) public {
        require(_owns(msg.sender, _tokenId));

        _approve(_tokenId, _to);
        emit Approval(msg.sender, _to, _tokenId);
    }

    function setApprovalForAll(address operator, bool approved) public {
        require(operator != msg.sender);

        _operatorApprovals[msg.sender] [operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function getApproved(uint256 tokenId) public view returns (address) {
        require(tokenId < kitties.length); //token must exist 

        return kittyIndexApproved[tokenId];

    }

    function isApprovedForAll(address owner, address operator) public view returns (bool) {
        return _operatorApprovals[owner] [operator];
    }

    function createKittyGen0(uint256 _genes) public onlyOwner returns (uint256) {
        require(gen0Counter < CREATION_LIMIT_GEN0);

        gen0Counter++;
        
        return _createKitty(0, 0, 0, _genes, msg.sender);
    }

    function _createKitty(
        uint256 _mumId,
        uint256 _dadId,
        uint256 _generation,
        uint256 _genes,
        address owner 
        
    ) private returns (uint256) {
        Kitty memory _kitty = Kitty({ 
            genes: uint64(_genes),
            birthTime: uint64(block.timestamp),
            mumId: uint32(_mumId),
            dadId: uint32(_dadId),
            generation: uint16(_generation)
        });
     
        kitties.push(_kitty);
        uint256 newKittenId = kitties.length -1; 

        emit Birth(owner, newKittenId, _mumId, _dadId, _genes);
 
        _transfer(address(0), owner, newKittenId);
    
        return newKittenId; 
    }
    function getKitty(uint256 kittyId) public view returns(uint256, uint64, uint32, uint32, uint16) {
        return 
        (
        kitties[kittyId].genes, 
        kitties[kittyId].birthTime, 
        kitties[kittyId].mumId, 
        kitties[kittyId].dadId,
        kitties[kittyId].generation
        );
    }
    function balanceOf(address owner) external  override view returns (uint256 balance) {
    return ownershipTokenCount[owner];
        
    }

    function totalSupply() external view returns (uint) {
        return kitties.length;
    }
  

    function ownerOf(uint256 _tokenId) external view returns (address) {
        require(kittyIndexToOwner[_tokenId] != address(0), "ownerOf: zero address");
           return kittyIndexToOwner[_tokenId];

      
    }

    function transfer(address _to, uint256 _tokenId) external override {
    

        require (_to != address(0));
        require (_to != address(this));
        require (_owns(msg.sender,_tokenId));

        _transfer(msg.sender, _to, _tokenId);
    }
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        ownershipTokenCount[_to]++;
    
    kittyIndexToOwner[_tokenId] = _to;

    if (_from != address (0)) {
        ownershipTokenCount[_from]--;
        delete kittyIndexApproved[_tokenId];
    }

    //Emit the transfer events
    emit Transfer(_from, _to, _tokenId); 

    }

    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return kittyIndexToOwner[_tokenId] == _claimant;
    }
    
    function _approve(uint256 _tokenId, address _approved) internal {
        kittyIndexToApproved[_tokenId] = _approved;
    }
    function _approvedFor(address _claimnat, uint256 _tokenId) internal view returns (bool) {
        return kittyIndexToApproved[_tokenId] == _claimant;
    }
    function _checkERC721Support(address _from, address _to, uint256 _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);
        return returnData == MAGIC_ERC721_RECEIVED;

        //check return value

    }
    function _isContract(address _to) view internal returns (bool) {
        uint32 size;
        assembly{
            size := extcodesize(_to)
        }
        return size > 0;

    }
}

IERC721Receiver.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

interface IERC721Receiver {
    function onERC721Received(address operator, address from, uint tokenId, bytes calldata data) external returns (bytes4);

    }
2 Likes

nice work. to make your code more readable you could abstract those requirs to a modifier to reduce duplicate coe and to tidy up the readability of your functions buisness logic

Kittycontract.sol

pragma solidity 0.8.0;
pragma abicoder v2;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./Ownable.sol";

contract Kittycontract is IERC721, Ownable {
    
    mapping(address => uint) OwnershipTokenCout;
    mapping(uint => address) public TokenIdOwner;
    mapping(uint => address) public kittyIndexToApproved;
    mapping(address => mapping(address => bool)) private _operatorApprovals;
    
    uint public constant Gen0SupplyLimit = 10;
    string public constant projectName = "CreativeKitties";
    string public constant projectSymbol = "CK";

    bytes4 internal constant MAGIC_ERC721_RECEIVED= bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

    event Birth(address owner, uint kittenId, uint momId, uint dadId, uint genes);

    struct Kitty {
        uint genes;
        uint64 birthTime;
        uint32 momId;
        uint32 dadId;
        uint16 generation;
    }

    Kitty[] kitties;

    uint gen0Counter = 0;

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external override {
        safeTransferFrom(_from, _to, _tokenId, "");
    }

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public override {
        require( msg.sender == _from || isApprovedForAll(_from, msg.sender) || kittyIndexToApproved[_tokenId] == msg.sender);
        require(_owns(_from, _tokenId) && _to != address(0) && _tokenId < kitties.length);

        _safeTransfer(_from, _to, _tokenId, _data);
    }

    function _safeTransfer(address _from, address _to, uint _tokenId, bytes memory _data) internal {
        _transfer(_from, _to, _tokenId);
        require( _checkERC721Support(_from, _to, _tokenId, _data) );

    }

    function transferFrom(address _from, address _to, uint256 _tokenId) external override {
        require( msg.sender == _from || isApprovedForAll(_from, msg.sender) || kittyIndexToApproved[_tokenId] == msg.sender);
        require(_owns(_from, _tokenId) && _to != address(0) && _tokenId < kitties.length);
        
        _transfer(_from, _to, _tokenId);
    }
    
    function approve(address _approved, uint256 _tokenId) external override {
        require(_owns(msg.sender, _tokenId) || kittyIndexToApproved[_tokenId] == msg.sender, "Unable to set approval");

        kittyIndexToApproved[_tokenId] = _approved;
        emit Approval(msg.sender, _approved, _tokenId);
    }

    function setApprovalForAll(address _operator, bool _approved) external override {
        require(_operator != msg.sender);

        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    function getApproved(uint256 _tokenId) external override view returns (address) {
        require(_tokenId < kitties.length, "TokenID doesn't exist");

        return kittyIndexToApproved[_tokenId];
    }

    function isApprovedForAll(address _owner, address _operator) public override view returns (bool) {
        return _operatorApprovals[_owner][_operator];
    }

    function getKittyInfo(uint catId) external view returns (Kitty memory , address owner) {
        Kitty storage kitty = kitties[catId];
        return (kitty, TokenIdOwner[catId]);
    }

    function createKittyGen0(uint _genes) public onlyOwner returns (uint) {
        require(gen0Counter < Gen0SupplyLimit, "Gen 0 supply fully minted");
        gen0Counter++;
        return _createKitty(_genes, 0, 0, 0, msg.sender);

    }

    function _createKitty (uint _genes, uint _momId, uint _dadId, uint _generation, address _owner) private returns (uint) {
        Kitty memory _newKitty = Kitty({
            genes: _genes,
            birthTime: uint64(block.timestamp),
            momId: uint32(_momId),
            dadId: uint32(_dadId),
            generation: uint16(_generation)
        });
        kitties.push(_newKitty);
        uint newCatId = kitties.length - 1;

        emit Birth(_owner, newCatId, _momId, _dadId, _genes);

        _transfer(address(0), _owner, newCatId);
       
        return newCatId;
    }

    function balanceOf(address owner) external override view returns (uint256 balance) { 
        return OwnershipTokenCout[owner];
    }

    function totalSupply() external override view  returns (uint256 total) {
        return kitties.length;
    }

    function name() external override view returns (string memory tokenName) {
        return projectName;
    }
  
    function symbol() external override view returns (string memory tokenSymbol) {
        return projectSymbol;
    }

    function ownerOf(uint256 tokenId) external override view returns (address owner) {
        return TokenIdOwner[tokenId];
    }
  
    function transfer(address _to, uint256 _tokenId) external override {
        require(_to != address(0), 'Cant send to 0 address');
        require(_to != address(this), 'Cant send to this contract');
        require(_owns(msg.sender, _tokenId), 'You dont own this token'); 

        _transfer(msg.sender, _to, _tokenId);
    }

    function _transfer(address _from, address _to, uint _tokenId) internal {
        OwnershipTokenCout[_to] ++;
        TokenIdOwner[_tokenId] = _to;

        if (_from != address(0)){
            OwnershipTokenCout[_from] --;
            delete kittyIndexToApproved[_tokenId];
        }
        
        emit Transfer(_from, _to, _tokenId);
    }

    function _owns(address _claimant, uint _tokenId) internal view returns(bool) {
        return TokenIdOwner[_tokenId] == _claimant;
    }

    function _checkERC721Support(address _from, address _to, uint _tokenId, bytes memory _data) internal returns(bool) {
        if( !_isContract(_to)) {
            return true;
        }
        
        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
        return returnData == MAGIC_ERC721_RECEIVED;

    }

    function _isContract(address _to) internal view returns(bool) {
        uint32 size;
        assembly{
            size := extcodesize(_to)
        }
        return size > 0;
    }
    

}

IERC721Receiver.sol

pragma solidity 0.8.0;

interface IERC721Receiver {
    function onERC721Received(address operator, address from, uint tokenId, bytes calldata data) external returns (bytes4);
}
2 Likes