Assignment - Safetransfer Implementation

SafeTransferFrom function assignment:

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public {
        require(ownerOf(_tokenId) == _from, "the sender address do not own this token");
        require(msg.sender == _from || _operatorApprovals[_from][msg.sender] == true || catIndexToApproved[_tokenId] == msg.sender, "You do not have the required approvals for this action!");
        require(_to != address(0), "ERC721: transfer to the zero address");
        require(_tokenId < cats.length, "This cat doesn't exist!");
        _safeTransfer(_from, _to, _tokenId, _data);
    }

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
        safeTransferFrom(_from, _to, _tokenId, "");
    }
1 Like

Same question:
Why is the requires statement after the transfer function???

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

3 Likes

Here is my code.

assignment: SafeTransfer Implementation
function transferFrom(address _from, address _to, uint256 _tokenId) external {
        require(msg.sender == _from || 
                _operatorApprovals[owner][msg.sender] == true||
                _approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId), "Address does NOT own this token");
        require(_to != address(0), "You CANNOT transfer to address(0)");
        require(_tokenId < kitties.length, "Does NOT exist - Token is not valid"); // Token must exist

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

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        require(msg.sender == _from || 
                _operatorApprovals[owner][msg.sender] == true||
                _approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId), "Address does NOT own this token");
        require(_to != address(0), "You CANNOT transfer to address(0)");
        require(_tokenId < kitties.length, "Does NOT exist - Token is not valid"); // Token must exist

        _safeTransfer(msg.sender, _to, _tokenId, "");
    }
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
        require(msg.sender == _from || 
                _operatorApprovals[owner][msg.sender] == true||
                _approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId), "Address does NOT own this token");
        require(_to != address(0), "You CANNOT transfer to address(0)");
        require(_tokenId < kitties.length, "Does NOT exist - Token is not valid"); // Token must exist

        _safeTransfer(msg.sender, _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));
    }
1 Like

Hi guys,

Iā€™m getting error that all function should have override function in header of the funcion, how come Filip is not getting that compile error?

cmd
C:\Users\kamil\Documents\Ethereum-201\CryptoDoggies4-main>truffle compile

Compiling your contracts...
===========================
> Compiling .\contracts\IERC721.sol
> Compiling .\contracts\IERC721Receiver.sol
> Compiling .\contracts\cryptodoggies.sol  

> Compilation warnings encountered:

    Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol

,Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721Receiver.sol

,Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol


TypeError: Overriding function is missing "override" specifier.
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:83:5:
   |
83 |     function balanceOf(address owner) public view returns (uint256 balance) {
   |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:24:5:
   |
24 |     function balanceOf(address owner) external view returns (uint256 balance);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:87:5:
   |
87 |     function totalSupply() public view returns (uint256 total){
   |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:29:5:
   |
29 |     function totalSupply() external view returns (uint256 total);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:91:5:
   |
91 |     function ownerOf(uint256 tokenId) public view returns (address) {
   |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:48:5:
   |
48 |     function ownerOf(uint256 tokenId) external view returns (address owner);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:106:5:
    |
106 |     function transfer(address _to, uint256 _tokenId) public {
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:62:5:
   |
62 |     function transfer(address to, uint256 tokenId) external;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:140:5:
    |
140 |     function approve(address _approved, uint256 _tokenId) public {
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:70:5:
   |
70 |     function approve(address _approved, uint256 _tokenId) external;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:153:5:
    |
153 |     function getApproved(uint256 _tokenId) public view returns (address) {
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:84:5:
   |
84 |     function getApproved(uint256 _tokenId) external view returns (address);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:166:5:
    |
166 |     function setApprovalForAll(address _operator, bool _approved) public {
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:78:5:
   |
78 |     function setApprovalForAll(address _operator, bool _approved) external;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Overriding function is missing "override" specifier.
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:177:5:
    |
177 |     function isApprovedForAll(address _owner, address _operator) public view returns (bool){
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overridden function is here:
  --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:90:5:
   |
90 |     function isApprovedForAll(address _owner, address _operator) external view returns (bool);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

,TypeError: Contract "DoggiesContract" should be marked as abstract.
 --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/cryptodoggies.sol:7:1:
  |
7 | contract DoggiesContract is IERC721, Ownable {
  | ^ (Relevant source part starts here and spans across multiple lines).
Note: Missing implementation:
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:112:5:
    |
112 |     function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: Missing implementation:
   --> /C/Users/kamil/Documents/Ethereum-201/CryptoDoggies4-main/contracts/IERC721.sol:104:5:
    |
104 |     function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external;      
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      

Compilation failed. See above.
Truffle v5.3.7 (core: 5.3.7)
Node v16.2.0
DoggieContract.sol
pragma solidity ^0.8.7;

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

contract DoggiesContract is IERC721, Ownable {

    uint public constant CreationLimitGen0 = 30000;
    string public constant override name = "Crypto Doggies";
    string public constant override symbol = "CD";
    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

    event Birth(
        address owner, 
        uint doggieId, 
        uint mumId, 
        uint dadId, 
        uint genes
    );

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

    Doggie[] doggies;

    mapping (uint256 => address) public doggieIndexToOwner;
    mapping (address => uint) ownershiptokenCount;
    mapping (uint256 => address) public doggieIndexToApprove;
    mapping (address => mapping(address => bool)) public _operatorApprovals;

    uint public gen0Counter;

    function getDoggie(uint _doggieId) public view returns(uint, uint64, uint32, uint32, uint16){
        Doggie storage doggieToReturn = doggies[_doggieId];
        return (
            doggieToReturn.genes, 
            doggieToReturn.birthTime, 
            doggieToReturn.mumId, 
            doggieToReturn.dadId, 
            doggieToReturn.generation
            );
    }

    function createDoggieGene0(uint _genes) public onlyOwner{
        require(gen0Counter < CreationLimitGen0);

        gen0Counter++;

        _createDoggie(0, 0, 0, _genes, msg.sender);
    }

    function _createDoggie(
        uint _mumId,
        uint _dadId,
        uint _generation,
        uint _genes,
        address _owner
    )internal returns(uint) {
        Doggie memory _doggie = Doggie({
            genes: _genes,
            birthTime: uint64(block.timestamp),
            mumId: uint32(_mumId),
            dadId: uint32(_dadId),
            generation: uint16(_generation)
        });
        doggies.push(_doggie);
        uint newDoggieId = doggies.length -1;
        emit Birth(_owner, newDoggieId, _mumId, _dadId, _genes);

        _transfer(address(0), _owner, newDoggieId);

        return newDoggieId;
        
    }

    
    function balanceOf(address owner) public view returns (uint256 balance) {
        return ownershiptokenCount[owner];
    }
    
    function totalSupply() public view returns (uint256 total){
        return doggies.length;
    }

    function ownerOf(uint256 tokenId) public view returns (address) {
        return doggieIndexToOwner[tokenId];
    }

    /* @dev Transfers `tokenId` token from `msg.sender` to `to`.
     *
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `to` can not be the contract address.
     * - `tokenId` token must be owned by `msg.sender`.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address _to, uint256 _tokenId) public {
        require(_to != address(0));
        require(_to != address(this));
        require(_owes(msg.sender, _tokenId));

        _transfer(msg.sender, _to, _tokenId);

    }

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

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

        emit Transfer(_from, _to, _tokenId);


    }
    
    function _owes(address _claimant, uint _tokenId) internal view returns(bool) {
        doggieIndexToOwner[_tokenId] == _claimant;

    }

    /// @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) public {
        require(_owes(msg.sender, _tokenId) || _operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true);
        
        doggieIndexToApprove[_tokenId] = _approved;
            
        emit Approval(msg.sender, _approved, _tokenId);
                 
    }
    
    /// @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) public view returns (address) {
        require(_tokenId < doggies.length);
        
        return doggieIndexToApprove[_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) public {
        require(_operator != msg.sender);

        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _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 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) public override {
        require(msg.sender == _from || _operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true || doggieIndexToApprove[_tokenId] == msg.sender);
        require(_owes(_from, _tokenId));
        require(_to != address(0));
        require(_to != address(this));
        require(_tokenId < doggies.length);

        
        _transfer(_from, _to, _tokenId);
        
    }

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

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

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

hereā€™s my code:

function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) public override{
        require(msg.sender == _from || _operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true || doggieIndexToApprove[_tokenId] == msg.sender);
        require(_owes(_from, _tokenId));
        require(_to != address(0));
        require(_to != address(this));
        require(_tokenId < doggies.length);

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

function safeTransferFrom(address _from, address _to, uint256 _tokenId) public override{
        require(msg.sender == _from || _operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true || doggieIndexToApprove[_tokenId] == msg.sender);
        require(_owes(_from, _tokenId));
        require(_to != address(0));
        require(_to != address(this));
        require(_tokenId < doggies.length);

        _safeTransfer(_from, _to, _tokenId, "");
    }
1 Like

yes I changed that so it is on top, can please someone confirm this is an error in Filipā€™s code

@Kamil37 this is most probably because you are using different solidity version from the course.

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