Assignment - ERC721 Fulfillment transferFrom

heya!!
that’s my assignment…

function transferFrom(address _from, address _to, uint256 _tokenId) external override{
      require(_tokenId < kitties.length,'Invalid token Id');
      require(_to != address(0),"Receiver address should not be address(0)");
      require(owns(msg.sender,_tokenId) || operates(msg.sender,_tokenId) || operatesAll(tokenOwner[_tokenId],msg.sender),'Unauthorized tampering with transferFrom');
      require(_from == tokenOwner[_tokenId],"Sender account doesn't belong to token owner");
      _transfer(_from,_to,_tokenId);
   }
function _transfer(address from, address to, uint tokenId) internal{
       //from
       if(from != address(0)){
        ownershipTokenCount[from] -= 1;
        delete kittyIndexToApproved[tokenId]; //nice point
       }
       
       //to
       ownershipTokenCount[to] += 1;
       tokenOwner[tokenId] = to;

       emit Transfer(from,to,tokenId);
   }
function owns(address claimant, uint tokenID)internal view returns(bool){
      return (tokenOwner[tokenID] == claimant);
   }

   function operates(address _approved,uint _tokenId) internal view returns(bool){
      return kittyIndexToApproved[_tokenId] == _approved;
   }
   function operatesAll(address _owner, address _operator) internal view returns (bool){
     return _operatorApprovals[_owner][_operator];
   }

2 Likes

transferFrom function

  function transferFrom(address _from, address _to, uint256 _tokenId) external override payable{
      
      require(_tokenId < kitties.length,'Invalid token Id');
      require(_to != address(0),"Receiver cannot have address(0)");

      require(_owns(msg.sender,_tokenId) || operates(msg.sender,_tokenId) || operatesAll(kittyIndexToApproved[_tokenId],msg.sender),'Incorrect use of transferFrom');
      require(_owns(msg.sender, _tokenId),"msg.sender Is Not Token Owner!");
      
      _transfer(_from,_to,_tokenId);
   }
1 Like

transferFrom assignment:

function transferFrom(address _from, address _to, uint256 _tokenId) override external {
    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");
    _transfer(_from, _to, _tokenId);
}
1 Like
    function transferFrom(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.");

        _transfer(_from, _to, _tokenId);
    }
1 Like
// Transfer ownership of an NFT
    function transferFrom(address _from, address _to, uint256 _tokenId) external {
        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!");
        _transfer(_from, _to, _tokenId);
    }
1 Like

Here is my code…

assignment fransferFrom()
function transferFrom(address _from, address _to, uint256 _tokenId) external {
        require(msg.sender == _from || 
                _operatorApprovals[owner][msg.sender] == true||
                msg.sender == kittyIndexToApproved[_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");

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

Note: I found a problem when I went to compile, so I fixed it and made edit to my code above.

1 Like

ok I did it a bit differently and not sure if it is correct and would work. Everyone else code looks much simpler :laughing:
anyway here it is, please let me know if that would work fine?

function transferFrom(address _from, address _to, uint256 _tokenId) external override {
        require(_owes(_from, _tokenId));
        require(_to != address(0));
        require(_to != address(this));
        require(_tokenId < doggies.length);

        if (_owes(msg.sender, _tokenId)) {
            _transfer(_from, _to, _tokenId);
        }

        else if (_operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true) {
            _transfer(_from, _to, _tokenId);
        }

        else if (doggieIndexToApprove[_tokenId] == msg.sender) {
            _transfer(_from, _to, _tokenId);
        }
    }

EDIT: I did correct my function so it is like that now (but is still wonder if above one would work?):

function transferFrom(address _from, address _to, uint256 _tokenId) external 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);
        
    }

ok I think in Filip video there is a mistake in transferFrom code

function isApprovedForAll is only a getter function and it can be either true or false so it doesn’t require anything it should check if it is equal to true.

Is that correct?

Filip’s code

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

        _transfer(_from, _to, _tokenId);
}

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

Yeah thats correct!
isApprovedForAll is just for checking global approval.

transferFrom function
    function _exists(uint256 tokenId) internal view returns (bool) {
        return kittyIndexToOwner[tokenId] != address(0);
    }

    function transferFrom(address from, address to, uint256 tokenId) external virtual override {
        require(_exists(tokenId), "Token does not exist");
        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(to != address(0), "Cannot send to a zero address");
        require(to != address(this), "Cannot transfer to this contract");

        _transfer(from, to, tokenId);
    }

Assignment:

function transferFrom(address _from, address _to, uint256 _tokenId) external {
        require(_isApprovedOrOwner(_msgSender(), _tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(_from, _to, _tokenId);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }
function _exists(uint256 tokenId) internal view returns (bool) {
        return kittyIndexToOwner[tokenId] != address(0);
    }
function getApproved(uint256 _tokenId) public view returns (address){
        require(_exists(_tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[_tokenId];
    }
1 Like

I wrote my code slightly differently.

    /// @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 {

        require(ownership[_tokenId] == msg.sender || approveToken[_tokenId] == msg.sender 
            || operatorApproval[_from][msg.sender] == true, "Not owner, nor approved, nor operator");
        require(ownership[_tokenId] == _from, "_from address is not the owner");
        require(_tokenId < allTokens.length, "Token does not exist");
        require(_to != address(0));

        _transfer(_from, _to, _tokenId);
    }

I have two questions?
Won’t the function automatically throw if ‘_from’ is not the owner?
How does the caller confirm or check whether the receiver can receive NFTs?

Regards
Em

1 Like

hey @Emmerich we are already checking that _from is not the owner when we use the msg.sender .
require(ownership[_tokenId] == msg.sender || approveToken[_tokenId] == msg.sender

Can someone here help me understand a couple core concepts?

  1. I followed Filip’s method of using an external function for checks, then having that external function call an internal “underscore” _function for the actual execution. I noticed that Filip places the emit keyword in the first (external) function. I placed mine in the second (internal) function. Is there anything wrong with doing it that way? What is the best practice and why?

  2. My compiler is suggesting that I change the name() and symbol() view functions to pure. Is there any reason to do that?

  3. In the case of this project, is there any reason to implement the functions in the IERC721 Interface rather than simply declaring them all in the base contract?

pragma solidity 0.8.0;

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

contract Kittycontract is Ownable {


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

    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 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;
    }

}

Nothing wrong on it :slight_smile: , filip just made it for the example, there is no best practice for the events, rather than use them when you want/need. Most of the contracts have the events(or emit them), on their internal or private functions, just as a measure on which announce the result.

Because those 2 functions does not change the state of any variable, rather than returning the existing one. So pure is used when the functions does not change/modify any variable of the contract.

Nothing wrong with it, still is a good practice to have an interface that describe which methods can be called for your contract, instead of looking at the contract, the interface will be easier to read.

Carlos Z

Thank you, Carlos! My compiler was giving me a hard time about defining functions in both the interface and the base contract, so wasn’t sure about the best way to handle that.

I still don’t really understand the difference between view and pure though. Because view also does not change the state of the variable. Since they both only ‘read’ from the contract without changing anything, in which situations is pure better than view?

My transferFrom function looks like this:

    function transferFrom(address _from, address _to, uint256 _tokenId) external override {
        if(_from != msg.sender && ownerOf(_tokenId) != msg.sender) {
            require(kittyIndexToApproved[_tokenId] == msg.sender || isApprovedForAll(ownerOf(_tokenId), msg.sender) == true);
        }
        require(_tokenId < kitties.length);
        require(_to != address(0));
        _transfer(_from, _to, _tokenId);
    }

The rest of this code is:

pragma solidity ^0.8.0;

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

contract Kittycontract is IERC721, Ownable {

    string private tokenName = "KittyToken";
    string private tokenSymbol = "KITTY";
    uint constant MAXGENZEROCATS = 2000;
    uint currentGenZeroCats;

    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 view returns (address) {
        require (_tokenId < kitties.length); // Token Must 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 override {
        if(_from != msg.sender && ownerOf(_tokenId) != msg.sender) {
            require(kittyIndexToApproved[_tokenId] == msg.sender || isApprovedForAll(ownerOf(_tokenId), msg.sender) == true);
        }
        require(_tokenId < kitties.length);
        require(_to != address(0));
        _transfer(_from, _to, _tokenId);
    }
}

Although it’s firing an error that suggests the Kittycontract should be marked as abstract… I’ll look into that…

1 Like
    function transferFrom(address _from, address _to, uint256 _tokenId) external {
        require((tokenOwner[_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");

        _transfer(_from, _to, _tokenId);     
    }
1 Like
transferFrom function
    function transferFrom(address _from, address _to, uint256 _tokenId) external {
        //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

        _transfer(_from, _to, _tokenId);
        emit fishyTransfer(_from, _to, _tokenId);
    }
2 Likes
function _isApprovedOrOwner(address _spender, uint256 _tokenId) internal returns (bool) {
    require(_exists(_tokenId), "TokenId does not exist!");
    address owner = owners[tokenId];
    return (_spender == owner || _tokenApprovals[_tokenId] == _spender || _operatorApprovals[owner][_spender]);
  }

function transferFrom(address _from, address _to, uint256 _tokenId) external {
    require(_to != address(0), "Reciever must not be dead address!");
    require(_isApprovedOrOwner(_from, _tokenId), "Not owner nor approved to transfer the NFT!");

    _transfer(_from, _to, _tokenId);
  }
2 Likes