Assignment - Safetransfer Implementation

Hey @RBeato it its the way you are calling your functions. Take a look of this example is the right implementation you need to use.
2021-07-18_19h47_51

1 Like

Hey @bjamRez the issue in your code is that you are calling the same function name.
Take a look to this example.
2021-07-18_19h47_51

Yes, thank you. I ended up re-doing the contract and realized I was reusing code, like you said.
Here is how I have it now. I got rid of the double quotes " ", where they replace bytes4 data, since I was still getting an error there. I donā€™t know if this is write though.

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


  function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external {
    require(_to != address(0), "Must transfer to zero address");
    require(_tokenId < kitties.length, "Token does not exist");
    require(_owns(_from, _tokenId), "Not a valid NFT, not the owner of NFT");

     //spender is _from (DEX) or spender is approved for tokenId or spender is operator for _from
    require(msg.sender == _from || isApprovedForAll(_from, _to) || _approvedFor(msg.sender, _tokenId));

    _safeTransfer(_from, _to, _tokenId, data);

  }


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

Actually I think it doesnā€™t really matter if the require statement after _transfer function fails then the entire transaction 'll be reverted.

function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external {
        require(_to != address(0));
        require((msg.sender == _from || _approvedFor(_from, _tokenId) || isApprovedForAll(_from, msg.sender)));
        require(_owns(msg.sender, _tokenId));
        require(_tokenId < kitties.length);
        _safeTransfer(_from, _to, _tokenId, data);
    }
    
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external{
        require(_to != address(0));
        require((msg.sender == _from || _approvedFor(_from, _tokenId) || isApprovedForAll(_from, msg.sender)));
        require(_owns(msg.sender, _tokenId));
        require(_tokenId < kitties.length);
        bytes memory data = "";
        _safeTransfer(_from, _to, _tokenId, data);
    }
    
    function transferFrom(address _from, address _to, uint256 _tokenId) external{
        require(_to != address(0));
        require((msg.sender == _from || _approvedFor(_from, _tokenId) || isApprovedForAll(_from, msg.sender)));
        require(_owns(msg.sender, _tokenId));
        require(_tokenId < kitties.length);
        _transfer(_from, _to, _tokenId);
    }

    function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns(bool) {
        if(!_isContract(_to)) {
            return true;
        }
        //onERC721Received 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;
    }

I see that in the implementation video the function
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external;

is implemented as public where as in IERC721 interface its signature is external so is it allowed? and secondly the same function is called within function safeTransferFrom(address _from, address _to, uint256 _tokenId) external

so is it valid to call a external or public function from within contract?

@thecil @kenn.eth @Bhujanga

Hello Devs, I am really struggling here with creating the Catalogue page where all of the Gen0 Kittens will live.
I did get some help from Mauro as he showed me the code but it is enormous. I have something going already but I donā€™t know where I can post for this Catalogue sections? I donā€™t see any other users discussing eitherā€¦so Where can I post a question regarding the Catalogue?

2 Likes

Have a look at my repo, maybe it can help you and give you an idea.

2 Likes

hey @bjamRez ! For rendering multiple cats, you can create a function that contains the html from a cat body and append it to a div . Also to render each design, we need a function to select each catBox passing the genes property for the style.

Check out this example:

Html where we gonna append each cat

<div class="row" id="catGrid"></div>

Javascript Function

//Cat HTML
function appendCat(catId,genes){
//Notice that im using this special quotes `  ` in order to use line break when defining the variable.
    var catHtml = `<div class="col-sm-4" id="cat`+  catId + `">                
                         <div id="head`+  catId + `"></div>
                         <div id="body`+  catId + `"></div>
                         <div id="paw`+  catId + `"></div>
                         <div id="tail`+  catId + `"></div>
                     Āæ</div> `

   //append cat to div
     $("catGrid").append(catHtml)
   //To render the cat design
       renderCat(catId,genes)
}

The function renderCat(catId,genes) uses the catId to select the ā€œcatā€ + catId and each part inside the new cat box you just append to the div. Also the functions from factory that apply CSS changes for each design, will have to point to each Box with the given catId

For example:


function renderCat(catId,genes){
//each of this design functions will point to the right Box and apply CSS to it.
      headColor(catId,genes.headColor)
      eyeShape(catId,genes.eyeShape)
//and so on...

}

Hopes this help.
You can also check out my explanation in this other topic

2 Likes

Thanks Ken, truly appreciate the help. I will continue using this added bit of knowledge

heya !!
I was also facing the same error ā€œNo matching declaration found after argument-dependent lookup.ā€. I couldnā€™t find a workaround, so I changed call data to memory for data parameter.
@thecil is it good to go or there could be a better solution.
Also, Iā€™m using a modifier checkTransfer for require statements here.

IERC721Receiver.sol

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

kittyContract.sol - just adding the snippets for new changes

 modifier checkTransfer(address _from, address _to, uint256 _tokenId){
      require(_tokenId < kitties.length,'Invalid token Id');
      require(_to != address(0),"Receiver address should not be address(0)");
      require(_from == tokenOwner[_tokenId],"Sender account doesn't belong to token owner");
      require(msg.sender == _from || operates(msg.sender,_tokenId) || isApprovedForAll(_from,msg.sender),'Unauthorized tampering with transferFrom');
      _;
   }
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public virtual override {
     safeTransferFrom(_from,_to,_tokenId,"");
   }

   function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public virtual override checkTransfer(_from,_to,_tokenId){
       _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));
   }

1 Like

Hey @tanu_g, hope you are well.

Would be great if you can share your project on a github repo, I would like to download and replicate the issue from my side to understand it better :face_with_monocle:

Carlos Z

1 Like

@thecil. Thanks for your support.
hereā€™s the Kitty Contract

Steps to reproduce the error:

  1. Replace this
//@param bytes memory data
 function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public virtual override checkTransfer(_from,_to,_tokenId){
       _safeTransfer(_from,_to,_tokenId,data);
   }

with this

//@param bytes calldata data
 function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) public virtual override checkTransfer(_from,_to,_tokenId){
       _safeTransfer(_from,_to,_tokenId,data);
   }
1 Like

Calldata is only valid for parameters of external contract functions and is required for this type of parameter. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory

https://docs.soliditylang.org/en/v0.7.4/types.html?highlight=calldata#data-location

Your contract compiles perfectly if we use memory instead of calldata.

image

Carlos Z

1 Like

Thanks, @thecil.
So, the conclusion is here we can use memory data location in this function as this function is not coming from an external contract.

Correct, the error from the compiler that you mention above, its probably based on using the incorrect data location keyword and visibility level of this function :nerd_face:

Adding only the modifier should do the trick without any other error.

Carlos Z

1 Like

here is my safeTransferFrom function

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

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public override  {
       require(_to != address(0),"Receiver cannot have address(0)");
       require(msg.sender == _from || _approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender));
       require(_owns(_from, _tokenId));
       require(_tokenId < kitties.length);

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

safeTransferFrom functions:

function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) override public {
    require((msg.sender == kittyIndexToOwner[_tokenId]) || (_operatorApprovals[kittyIndexToOwner[_tokenId]][msg.sender] == true) || (kittyIndexToApproved[_tokenId] == msg.sender), "only the owner or approved address can perform this action");
    require(_from == kittyIndexToOwner[_tokenId], "This address is not the rightful owwner of this token");
    require(_to != address(0), "Cannot send to the zero address");
    require(_tokenId <= kitties.length, "Invalid token ID");
    require(_checkERC721Support(_from, _to, _tokenId, _data) );
    _transfer(_from, _to, _tokenId);
}

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

Hey there!

My code for this assignment:

Code
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external override{
        require(tokenExists[_tokenId] == true, "Token doesn't exist.");
        address owner = tokenIdOwners[_tokenId];
        require((tokenIdOwners[_tokenId] == msg.sender )||
            (kittyIndexToApproved[_tokenId] == msg.sender)||
            (operatorApprovals[owner][msg.sender] == true),
            "You need to be approved or the owner.");
        require(_from == tokenIdOwners[_tokenId], "_from needs to be the token owner.");
        require(_to != address(0), "_to can't be address 0.");

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

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external override{
        require(tokenExists[_tokenId] == true, "Token doesn't exist.");
        address owner = tokenIdOwners[_tokenId];
        require((tokenIdOwners[_tokenId] == msg.sender )||
            (kittyIndexToApproved[_tokenId] == msg.sender)||
            (operatorApprovals[owner][msg.sender] == true),
            "You need to be approved or the owner.");
        require(_from == tokenIdOwners[_tokenId], "_from needs to be the token owner.");
        require(_to != address(0), "_to can't be address 0.");

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

Have a question regarding the interface specification:
Why should the require of_checkERC721Support be called after _transfer? Wouldnā€™t it be safer to do it before?

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

My code

pragma solidity ^0.5.12;

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

contract KittyContract is IERC721, Ownable {
    event Birth(address owner, uint256 kittyId, uint256 mumId, uint256 dadId, uint256 genes);
    event ApprovalForAll(address owner, address operator, bool approved);

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

    mapping(address => uint256) ownerTokenCount;
    mapping(uint256 => address) tokenOwner;
    Kitty[] kittyList;
    mapping (uint256 => address) public kittyIndexToApproved;
    mapping (address => mapping(address => bool)) private _operatorApprovals;

    uint256 gen0Counter;
    uint256 constant CREATION_LIMIT_GEN0 = 10;
    bytes4 internal constant magicERC721Received = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

    constructor() public {
        gen0Counter = 0;
    }

    modifier validKittyId(uint256 kittyId) {
        require(kittyId < kittyList.length, "Invalid kitten id specified");
        _;
    }

    modifier ownerOrOperatorApprover(address from, uint256 _tokenId) {
        bool operatorApproved = _operatorApprovals[_ownerOf(_tokenId)][msg.sender];
        bool ownerApproved = msg.sender == _ownerOf(_tokenId);
        bool ownerAction = from == _ownerOf(_tokenId);
        require(operatorApproved || ownerApproved || ownerAction, "Only the owner/operator can approve");
        _;
    }

    function createKittyGen0(uint256 _genes) public onlyOwner {
        require(gen0Counter < CREATION_LIMIT_GEN0, "Generation zero limit exceeded");
        gen0Counter++;
        _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: _genes,
            birthTime: uint64(block.timestamp),
            mumId: uint32(_mumId),
            dadId: uint32(_dadId),
            generation: uint16(_generation)
        });
        kittyList.push(_kitty);
        uint256 newKittyId = kittyList.length;
        emit Birth(_owner, newKittyId, _mumId, _dadId, _genes);

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

        return newKittyId;
    }

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address _owner) external view returns (uint256 balance) {
        return ownerTokenCount[_owner];
    }

    /*
     * @dev Returns the total number of tokens in circulation.
     */
    function totalSupply() external view returns (uint256 total) {
        return kittyList.length;
    }

    /*
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory tokenName) {
        return "Walter's Crypto-Kitty";
    }

    /*
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory tokenSymbol) {
        return "WW_KITTY";
    }

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view validKittyId(tokenId) returns (address _owner) {
        return _ownerOf(tokenId);
    }

    function _ownerOf(uint256 tokenId) internal view returns (address _owner) {
        return tokenOwner[tokenId];
    }

    function transfer(address to, uint256 tokenId) external {
        require(to != address(0), "To address can't be zero");
        require(_ownerOf(tokenId) == msg.sender, "Only owner can transfer");
        _transfer(msg.sender, to, tokenId);
    }

    function _transfer(address from, address to, uint256 tokenId) internal {
        if (from != address(0)) {
            ownerTokenCount[from]--;
            delete kittyIndexToApproved[tokenId];
        }
        ownerTokenCount[to]++;
        tokenOwner[tokenId] = to;
    
        emit Transfer(from, to, tokenId);
    }

    function getKitty(uint256 kittyId) external view validKittyId(kittyId) returns (
        uint256 mumId, 
        uint256 dadId, 
        uint256 generation,
        uint256 genes,
        uint256 birthTime,
        address kittyOwner) {
        Kitty storage kitty = kittyList[kittyId];
        mumId = kitty.mumId;
        dadId = kitty.dadId;
        generation = kitty.generation;
        genes = kitty.genes;
        birthTime = kitty.birthTime;
        kittyOwner = _ownerOf(kittyId);
    }

    function approve(address _approved, uint256 _tokenId) external validKittyId(_tokenId) ownerOrOperatorApprover(_approved, _tokenId) {
        require(_approved != address(0), "Invalid approval address");
        _approve(_approved, _tokenId);
    }
    function _approve(address _approved, uint256 _tokenId) internal {
        kittyIndexToApproved[_tokenId] = _approved;
        emit Approval(msg.sender, _approved, _tokenId);
    }

    function setApprovalForAll(address _operator, bool _approved) external {
        require(_operator != msg.sender, "Invalid operator address");
        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    function getApproved(uint256 _tokenId) external view validKittyId(_tokenId) returns (address) {
        return kittyIndexToApproved[_tokenId];
    }

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

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

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) validKittyId(_tokenId) ownerOrOperatorApprover(_from, _tokenId) external {
        _safeTransfer(_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;
        }
        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, data);
        return returnData == magicERC721Received;
    }

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

    function transferFrom(address _from, address _to, uint256 _tokenId) validKittyId(_tokenId) ownerOrOperatorApprover(_from, _tokenId) external {
        require(_to != address(0), "Invalid to address");
        _transfer(_from, _to, _tokenId);
    }
}

1 Like