Assignment - ERC721 Fulfillment transferFrom

Implement the transferFrom function using the downloadable zip under the lecture.

Remember about using the correct cheks.

1 Like
transferFrom Assignment
    function transferFrom(address _from, address _to, uint256 _tokenId) external{
        require(msg.sender == _from || msg.sender == tokenIdToApproved[_tokenId] || _operatorApprovals[_from][msg.sender], "Only Owner, Operator or Approved Addresses can transfer!");
        require(_owns(_from, _tokenId), "Owner address is not connected to this token!");
        require(_to != address(0), "Transfer to zero-address is not possible!");
        require(_tokenId < bears.length, "This token does not exist!");

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

This is my code:

function transferFrom(address _from, address _to, uint256 _tokenId) external {
        require (_owns(_from, _tokenId));
        require (_owns(msg.sender, _tokenId) || 
                    _isApprovedForAll( kittyIndexToOwner[_tokenId], msg.sender) ||
                    kittyIndexToApproved[_tokenId] == msg.sender
                    , "Must own or operate or be approved to transfer");
        require (_to != address(0), "can't transfer to zero address");
        require(_tokenId < kitties.length, "Kitty does not exist");

        _transfer(_from,_to,_tokenId);
    }
1 Like

Here’s my code:

transferFrom (Doracontract.sol)

function transferFrom(address _from, address _to, uint256 _tokenId) external override {
  require(
    msg.sender == _from ||
    operatorApprovals[_from][msg.sender] == true ||
    msg.sender == tokenIndexToApproved[_tokenId],
    "ERC721: You don't have the permission to transfer token"
  );
  require(_owns(_from, _tokenId), "ERC721: The sender does not own this token");
  require(_to != address(0), "ERC721: Invalid address");
  require(tokenToOwner[_tokenId] != address(0), "ERC721: Invalid token ID");
  
  _transfer(_from, _to, _tokenId);
}
2 Likes

Hi All ( @AdamFortuna @Bhujanga @Vivek20 @REGO350 @kenn.eth )

I am having problems in compiling the contract with the following errors :point_down:

Screenshot 2021-05-09 at 6.39.42 PM

And Below is my Code :point_down:

pragma solidity 0.5.12;
import "./IERC721.sol";
import "./utils/SafeMath.sol";
import "./IERC721Receiver.sol";

contract Kittycontract is IERC721 {

using SafeMath for uint;

//constructors and Modifiers
constructor() public {
  contractOwner = msg.sender;
  }

modifier onlyOwner() {
  require(msg.sender == contractOwner);
  _;
  //continue execution
}

//events
event Birth(
  address owner,
  uint256 genes,
  uint256 newKittenId,
  uint256 mumId,
  uint256 dadId);

event Transfer(address indexed oldOwner, address indexed newOwner, uint256 indexed tokenId);
event Approval(address indexed Owner, address indexed approvedTo, uint256 indexed tokenId);
event ApproveAll(address indexed Owner, address operator, bool success);

//Struct- it gets stored in storage on EVM
struct Cat {
    uint256 genes;
    uint64 birthTime;
    uint32 mumId;
    uint32 dadId;
    uint16 generation;
}

//Mappings
//       owner    number of Cats
mapping(address => uint256) public tokenOwnershipCount;
//      tokenId     owner
mapping(uint256 => address) public ownershipCertificate;
// approval mapping
mapping(uint256 => address) public kittyIndexToApprove;
// approval all (operator) mapping
//[MYAADDR][OPERATORADDR] => TRUE/FALSE;
mapping(address => mapping(address => bool)) private _operatorApprovals;



//State variables
address public contractOwner;
string private constant Name = "MEOW-CATS";
string private constant Symbol = "MEOW";
uint public constant CREATION_LIMIT_GEN0 = 100;
uint public gen0Counter = 0;
bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

//arrays
Cat[] public cats;

//FUNCTIONS START FROM HERE :

//Function when called from external contract returns whether this contract support IERC721 or IERC165 standard?
function supportsInterface(bytes4 _interfaceId) external view returns(bool) {
  return ( _interfaceId == _INTERFACE_ID_ERC165 || _interfaceId == _INTERFACE_ID_ERC721);
}

//##############################################################################################################


// MAIN TRANSFER AND APPROVAL FUNCTIONS
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
  return _operatorApprovals[_owner][_operator];
}

function isApprovedOrOwner(address _spender, address _from, address _to, uint256 _tokenId) internal view returns(bool) {
  require(_to != address(0));
  require(_to != address(this));
  require(_owns(_from, _tokenId));
  require(_tokenId < cats.length);
  return (_spender == _from || approvedFor(_spender, _tokenId) || isApprovedForAll(_from, _spender));
}


//Function executing an External Call from another User/ Contract(without Data):
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
  safeTransferFrom(_from, _to, _tokenId, " ");
}

//Function executing an External Call from another User/ Contract(called with @param bytes calldata _data):
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
  require(isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
  _safeTransfer(_from, _to, _tokenId, _data);
}


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

//Function executing an External Call from another User/ Contract:
function transferFrom(address _from, address _to, uint256 _tokenId) external {
  require(isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
  //require(_to != address(0));
  //require(_to != address(this));
  //require(_owns(_from, _tokenId));
  //require(msg.sender == _from || approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender));
  //require(_tokenId < cats.length);
  _transfer(_from, _to, _tokenId);

}

//Function executing an External Call from another User/ Contract:
function transfer(address _to, uint256 _tokenId) external {
  require(_to != address(0));
  require(_to != address(this));
  require(_owns(msg.sender, _tokenId));

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

//Actual transfer of the token happens from this below Internal Function:
function _transfer(address _from, address _to, uint256 _tokenId) internal {
  tokenOwnershipCount[_to]++ ;
  if(_from != address(0)){
    tokenOwnershipCount[_from]-- ;
    delete(kittyIndexToApprove[_tokenId]);
  }

  //Event Transfer to be emitted now :
  emit Transfer(_from, _to, _tokenId);
}


//##########################################################################################################



//BASIC CAT CREATION AND GETTER FUNCTIONS
function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256){
  require(gen0Counter < CREATION_LIMIT_GEN0);
    gen0Counter ++;

  return _createKitty(0,0, _genes, 0, msg.sender);

}


function _createKitty (
  uint256 _mumId,
  uint256 _dadId,
  uint256 _genes,
  uint256 _generation,
  address _owner
) private returns(uint256) {

  Cat memory _cat = Cat({
    genes : _genes,
    birthTime : uint64(now),
    mumId : uint32(_mumId),
    dadId : uint32(_dadId),
    generation : uint16(_generation)
  });
  uint256 newKittenId = cats.push(_cat) - 1;

  //Event Birth to  be emitted :
  emit Birth(_owner, _genes, newKittenId, _mumId, _dadId);

  _transfer(address(0), _owner, newKittenId);
  return newKittenId;


}

function getKitty(uint256 _newKittenId) external view returns(
uint256 _mumId,
uint256 _dadId,
uint256 _genes,
uint256 _generation,
uint256 _birthTime,
address _owner){

  Cat storage cat = cats[_newKittenId];
  _genes = cat.genes;
  _dadId = uint256(cat.dadId);
  _mumId = uint256(cat.mumId);
  _generation = uint256(cat.generation);
  _birthTime = uint256(cat.birthTime);
  _owner = ownershipCertificate[_newKittenId];

}


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

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

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


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

function ownerOf(uint256 tokenId) external view returns (address owner) {
  return ownershipCertificate[tokenId];
}



//This below Function called "_owns" gives bool result whether the address has actual ownership of the _tokenId:
function _owns(address _claimant, uint256 _tokenId) internal view returns(bool) {
  return ownershipCertificate[_tokenId] == _claimant;
}



// APPROVE FUNCTION & GETTER FUNCTIONS RELATED TO APPROVALS :
//External function approving the _to address as _approved:
function approve(address _to, uint256 _tokenId) external {
  require(_owns(msg.sender, _tokenId));
  _approve(_to, _tokenId);
  //emit event Approve
  emit Approval(msg.sender, _to, _tokenId);
}

function _approve(address _approved, uint _tokenId ) internal {
  _approved = kittyIndexToApprove[_tokenId];
}

function getApproved(uint256 _tokenId) external view returns (address) {
  require(_tokenId < cats.length); //Token must exist
  return kittyIndexToApprove[_tokenId];
}

function setApprovalForAll(address _operator, bool approved) external {
  require(_operator != msg.sender);
  approved = _operatorApprovals[msg.sender][_operator];
  //emit event ApproveAll
  emit ApproveAll(msg.sender, _operator, approved);
}


function approvedFor(address _claimant, uint256 _tokenId) internal view returns(bool) {
  return kittyIndexToApprove[_tokenId] == _claimant;
}


// INTERNAL FUNCTION ASKING FOR ERC 721 SUPPORT  :
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);
  // Call on onERC721Received in the _to contract
  return returnData == MAGIC_ERC721_RECEIVED;
  // Check on Return Value;
}

// IF SIZE = O =>ITS A USER WALLET, IF > 0 => ITS A CONTRACT :
function _isContract(address _to) internal view returns(bool) {
  uint32 size;
  assembly{
    size := extcodesize(_to)
  }
  return size > 0;
}




}

Can someone Help me where I am going wrong? I am unable to figure it out ?

Thanks and Regards

Su.kal Crypto

1 Like

I do not have lots of time right now to look into it thoroughly, but feel free to look into my repo here and compare your code. My project is not yet finished, the last section is still in progress, but it at least compiling correctly.
From what I can see at first glance, I recommend updating your node and truffle. But this is not directly related to your compiling issue.

Hope you can fix it!

1 Like

Hi @Su.kal.Crypto,

I had a quick look and your function is defined external. As such you cannot call it from within your contract. Probably that’s what is causing the error.

1 Like

I agree with @Vivek20
According to the error message, it says that isApprovedForAll is not visible at this point. So it has to do something with visibility.

1 Like

@REGO350 @Bhujanga @AdamFortuna @Vivek20

Thanks a Ton to all of you, such a wonderful wonderful community always willing to help each Other, I am grateful :pray: :pray: :pray:

I made some silly mistakes in declaring functions as external , whereas they should have been public and in one case internal, now everything is compiling perfectly

Thanks a Ton again guys

Suveett Kalra (su.kal Crypto)

2 Likes

very tricky my code for the function

function transferFrom(address _from, address _to, uint256 _tokenId) external payable override {
        require(_to != address(0)); 
        require(_to != address(this)); 
        require(_owns(msg.sender, _tokenId) || kittyIndexToApproved[_tokenId] == msg.sender || _operatorApprovals[_from][msg.sender] == true);
        //Throws unless `msg.sender` is the current owner or is the approved address for this NFT or  is an authorized operator
        require(_owns(_from, _tokenId)); //Throws if `_from` is not the current owner
        require(_tokenId < kitties.length, "the tokenID does not exist"); 
        _transfer(_from,  _to,  _tokenId);
    }

My test
KittyContract_test.js

const KittyContract = artifacts.require("KittyContract")
const AssertionError = require('assertion-error');
const truffleAssert = require('truffle-assertions');
const assert = require('assert');

contract(KittyContract, accounts => {
    it(" transferFrom is not possible there is no token 0 ", async () => {
        let kittyContract = await KittyContract.deployed(); 
        await truffleAssert.reverts(
            kittyContract.transferFrom(accounts[0], accounts[1], 0)
        )
    })
    it("transferFrom msg.sender is the current owner", async () => {
        let kittyContract = await KittyContract.deployed();
        await truffleAssert.passes(
            kittyContract.createKittyGen0(1010961011462111)
        )
        await truffleAssert.passes(
            kittyContract.transferFrom(accounts[0], accounts[1], 0)
        )
    })
    it("transferFrom msg.sender is the approved address for token1", async () => {
        let kittyContract = await KittyContract.deployed();
        await truffleAssert.passes(
            kittyContract.createKittyGen0(1186338013672241)
        )
        await truffleAssert.passes(
            kittyContract.approve(accounts[1], 1, {from: accounts[0]} )
        )
        await truffleAssert.passes(
            kittyContract.transferFrom(accounts[0], accounts[2], 1, {from: accounts[1]})
        )
    })
    it("transferFrom msg.sender is an authorized operator address for token2", async() =>{
        let kittyContract = await KittyContract.deployed();
        await truffleAssert.passes(
            kittyContract.createKittyGen0(4622401822749011)
        )
        await truffleAssert.passes(
            kittyContract.setApprovalForAll(accounts[3], true, {from: accounts[0]})
        )
        await truffleAssert.passes(
            kittyContract.transferFrom(accounts[0], accounts[4], 2, {from: accounts[3]})
        )
    })
})

$ truffle test
Using network 'development'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



  Contract: function TruffleContract() {
      this.constructor = temp;
      return Contract.apply(this, arguments);
    }
    √  transferFrom is not possible there is no token 0  (1326ms)
    √ transferFrom msg.sender is the current owner (1114ms)
    √ transferFrom msg.sender is the approved address for token1 (1273ms)
    √ transferFrom msg.sender is an authorized operator address for token2 (1243ms)


  4 passing (6s)


Carlos@LAPTOP-14TAQHNV MINGW64 /e/Importante/Programas creados imagenes iso etc/Programas blockchain/Ethereum Truffle/Kitties_project (master)
$
2 Likes
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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


  abstract contract KittyContract is IERC721,  Ownable {

  using SafeMath for uint256;  

  mapping(uint256 => address) public kittyIndexToOwner; // an interger or index to an address
  mapping(address => uint256) ownershipTokenCount; // an address to a number, a count
  mapping(address => uint256[]) ownerToCats; //an address to a number of cats in an array
  mapping(uint256 => address) kittyIndexToApproved; // point to another address to approve of transfer a tokens on the behalf of the owner

  //operator approval: first owner allows second owner to spend on his behalf

  // MY ADDR => OPPERATOR ADDR => TRUE/FALSE
  // _oppratorApprovals[myAddr][operatorAddr] = true/false
  mapping(address => mapping(address => bool)) private _operatorApprovals;
  // implement different functions to set approval, get approval and set approval for all and get approval for all
  // ex. 
  // _operatorApprovals[myAddr][bobsAddr] = true;
  // _operatorApprovals[myAddr][aliceAddr] = false;
  // _operatorApprovals[myAddr][joesAddr] = true;

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

  // if made 'public constant', getter functions would be created
  // automatically, thus there would be no need to create getter functions
  // it's optional
  uint256 public constant CERATION_LIMIT_GEN0 = 10; // max num of cats to be generated
  uint256 public gen0Counter;
 

  string private _name;
  string private _symbol;

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

Kitty[] kitties;

  constructor(string memory name_, string memory symbol_) {
    _name = name_;
    _symbol = symbol_;
    owner = msg.sender;
}


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

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

  // could be external but externals can only be called from outside not within this contract
  function totalSupply() public view override returns (uint256 total) {
    return kitties.length;

  }

  function getAllCatsFor(address owner) external view returns (uint[] memory cats) {
    return  ownerToCats[owner];
  }


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

  function ownerOf(uint256 tokenId) external view override returns (address owner) {
    address _owner = kittyIndexToOwner[tokenId];
    require(_owner != address(0), "ERC721: owner query for nonexistent token");

    return _owner;
  }
  

  function getKitty(uint256 tokenId) external view returns(uint256 birthTime, uint256 mumId, uint256 dadId, uint256 generation, uint256 genes) {
    Kitty storage returnKitty = kitties[tokenId]; // storage is a pointer, instead of using memory - - we do not make a local copy of it
    return (uint256(returnKitty.birthTime), uint256(returnKitty.mumId), uint256(returnKitty.dadId), uint256(returnKitty.generation), uint256(returnKitty.genes));
  }

   function getKittyFilip(uint256 _id) public view returns(
     uint256 birthTime, 
     uint256 mumId, 
     uint256 dadId, 
     uint256 generation, 
     uint256 genes) {

    Kitty storage kitty = kitties[_id];

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

  function approve(address _approved, uint256 _tokenId) external override {
    address _owner = kittyIndexToOwner[_tokenId];
    require(msg.sender == address(0)); // Throws error unless `msg.sender`
    require(_approved != _owner); // The zero address indicates there is no approved address.
    require(_owns(msg.sender, _tokenId)); // Filip's - msg.sender is owner of tokenId

    _toApprove(_approved, _tokenId);
    emit Approval(msg.sender, _approved, _tokenId);

  }

  function getApproved(uint256 _tokenId) external view override returns (address) {
    require(_tokenId < kitties.length);
    // 0,1,2,3,4
    // lenght = 5;

    return kittyIndexToApproved[_tokenId];
  }

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

    emit ApprovalForAll(msg.sender, _operator, _approved);
  }

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

  }

   // available function to outside calls - it only sends from msg.sender to recipients
  function transfer(address to, uint256 tokenId) external override {
    require(to != address(this), "to cannot be the contract address" );
    require(to != address(0),"to cannot be the zero address" );
    require(_owns(msg.sender, tokenId));

    _transfer(msg.sender, to, tokenId);
    
    // might need to input _from instead of msg.sender to transfer from 0 address
    emit Transfer(msg.sender, to, tokenId);
     
  }

  function transferFrom(address _from, address _to, uint256 _tokenId) external override {
    /// 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

    require(msg.sender == owner); // Throws unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.
    require(_owns(_from, _tokenId)); // Throws if `_from` is not the current owner.
    require(_to != address(0)); // Throws if `_to` is the zero address.
    require( _tokenId < kitties.length); // Throws if `_tokenId` is not a valid NFT.

    _transfer(msg.sender, _to, _tokenId);

  }

  function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256){
    require(gen0Counter < CERATION_LIMIT_GEN0, "Gen 0 should be less than creation limit gen 0");

    gen0Counter++;

    // mum, dad and generation is 0
    // Gen0 have no owners; they are owned by the contract
   return  _createKitty(0,0,0, _genes, msg.sender); // msg.sender could also be -- address(this) - we are giving cats to owner

  }

  // create cats by generation and by breeding
  // retuns cat id
  function _createKitty(
    uint256 _mumId,
    uint256 _dadId,
    uint256 _generation, //1,2,3..etc
    uint256 _genes, // recipient
    address owner
  ) private returns(uint256) {
    Kitty memory newKitties = Kitty({ // create struct object
      genes: _genes,
      birthTime: uint64(block.timestamp),
      mumId: uint32(_mumId),
      dadId: uint32(_dadId),
      generation: uint16(_generation)
     });

     kitties.push(newKitties); // returns the size of array - 1 for the first cat

     uint256 newKittenId = kitties.length -1; // 0 -1

     emit Birth(owner, newKittenId, _mumId, _dadId, _genes);

     _transfer(address(0), owner, newKittenId); // birth of a cat from 0 (standard)

    return newKittenId; //returns 256 bit integer

  }

  // must transfer from address 0
  function _transfer(address from,  address to, uint256 tokenId) internal {

    ownershipTokenCount[to] = ownershipTokenCount[to].add(1);

    kittyIndexToOwner[tokenId] = to;

    // SEMD tokenId # => an address to a number of cats in an array
    ownerToCats[to].push(tokenId);
   
    // decrease token count from person A to person B
    if (from != address(0)) {
      ownershipTokenCount[from] = ownershipTokenCount[from].sub(1);
        _removeTokenIdFromOwner(from, tokenId);
        delete kittyIndexToApproved[tokenId]; 
    }
     
  }

    function _removeTokenIdFromOwner(address owner, uint256 tokenId) internal {
      uint256 lastId = ownerToCats[owner][ownerToCats[owner].length -1];
      for (uint256 i = 0; i < ownerToCats[owner].length; i++) {
        if (ownerToCats[owner][i] == tokenId) {
            ownerToCats[owner][i] = lastId;
            ownerToCats[owner].pop();
        }

      }

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

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

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


}
1 Like

I don’t know how important this is now but I ended up converting isApprovedForAll function into a local function without the depending on the virtual function:

 function _isApprovedForAll(address _owner, address _operator) view public returns (bool) {
    return _operatorApprovals[_owner][_operator];
  }
 require(msg.sender == _from || _approvedFor(msg.sender, _tokenId) || _isApprovedForAll(_from, msg.sender));
1 Like

@REGO350, @thecil, @Bhujanga
sorry, actually, this problem persists. I find that the IERC721 contract sometimes has parameters with underscores and sometime without.
From what I understand, this can cause problems as a contract will be seen as Abstract
I will actually need someone to look at my code that I will post.
I changed the IERC721 once so the contract functions matched the virtual ones and it worked previously but now it doesn’t

I am basically getting an error: “Should be marked as abstract”
but as if I do that, it will not migrate over

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

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


  contract KittyContract is IERC721, Ownable {

  using SafeMath for uint256;  

  mapping(uint256 => address) public kittyIndexToOwner; // an interger or index to an address
  mapping(address => uint256) ownershipTokenCount; // an address to a number, a count
  mapping(address => uint256[]) ownerToCats; //an address to a number of cats in an array
  mapping(uint256 => address) kittyIndexToApproved; // point to another address to approve of transfer a tokens on the behalf of the owner

  //operator approval: first owner allows second owner to spend on his behalf

  // MY ADDR => OPPERATOR ADDR => TRUE/FALSE
  // _oppratorApprovals[myAddr][operatorAddr] = true/false
  mapping(address => mapping(address => bool)) private _operatorApprovals;
  // implement different functions to set approval, get approval and set approval for all and get approval for all
  // ex. 
  // _operatorApprovals[myAddr][bobsAddr] = true;
  // _operatorApprovals[myAddr][aliceAddr] = false;
  // _operatorApprovals[myAddr][joesAddr] = true;

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

  // if made 'public constant', getter functions would be created
  // automatically, thus there would be no need to create getter functions
  // it's optional

  uint256 public constant CERATION_LIMIT_GEN0 = 10; // max num of cats to be generated
  uint256 public gen0Counter;
 

  string private _name;
  string private _symbol;

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

Kitty[] kitties;

  constructor(string memory name_, string memory symbol_) {
    _name = name_;
    _symbol = symbol_;
    owner = msg.sender;
}


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

  // could be external but externals can only be called from outside not within this contract
  function totalSupply() external view override returns (uint256 total) {
    return kitties.length;

  }

  function getAllCatsFor(address owner) external view returns (uint[] memory cats) {
    return  ownerToCats[owner];
  }


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

  function ownerOf(uint256 tokenId) external view override returns (address owner) {
    address _owner = kittyIndexToOwner[tokenId];
    require(_owner != address(0), "ERC721: owner query for nonexistent token");

    return _owner;
  }

  
  function getKitty(uint256 tokenId) public view returns(uint256 birthTime, uint256 mumId, uint256 dadId, uint256 generation, uint256 genes) {
    Kitty storage returnKitty = kitties[tokenId]; // storage is a pointer, instead of using memory - - we do not make a local copy of it
    return (uint256(returnKitty.birthTime), uint256(returnKitty.mumId), uint256(returnKitty.dadId), uint256(returnKitty.generation), uint256(returnKitty.genes));
  }

   function getKittyFilip(uint256 _id) public view returns(
     uint256 birthTime, 
     uint256 mumId, 
     uint256 dadId, 
     uint256 generation, 
     uint256 genes) {

    Kitty storage kitty = kitties[_id];

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



  function approve(address approved, uint256 tokenId) external override {
    address owner = kittyIndexToOwner[tokenId];
    require(msg.sender == address(0)); // Throws error unless `msg.sender`
    require(approved != owner); // The zero address indicates there is no approved address.
    require(_owns(msg.sender, tokenId)); // Filip's - msg.sender is owner of tokenId

    _toApprove(approved, tokenId);
    
    emit Approval(msg.sender, approved, tokenId);

  }

  function getApproved(uint256 tokenId) external view override returns (address) {
    require(tokenId < kitties.length);
    // 0,1,2,3,4
    // lenght = 5;

    return kittyIndexToApproved[tokenId];
  }

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

    emit ApprovalForAll(msg.sender, operator, approved);
  }

  function isApprovedForAll(address owner, address operator) external view override returns (bool) {
    // getter function
    // does this operator have approval from owner?
    return _operatorApprovals[owner][operator];
  }

   // available function to outside calls - it only sends from msg.sender to recipients
  function transfer(address to, uint256 tokenId) external override {
    require(to != address(this), "to cannot be the contract address" );
    require(to != address(0),"to cannot be the zero address" );
    require(_owns(msg.sender, tokenId));

    _transfer(msg.sender, to, tokenId);
    
    // might need to input _from instead of msg.sender to transfer from 0 address
    emit Transfer(msg.sender, to, tokenId);
  }


  function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256){
    require(gen0Counter < CERATION_LIMIT_GEN0, "Gen 0 should be less than creation limit gen 0");

    gen0Counter++;

    // mum, dad and generation is 0
    // Gen0 have no owners; they are owned by the contract
   return  _createKitty(0,0,0, _genes, msg.sender); // msg.sender could also be -- address(this) - we are giving cats to owner

  }

  // create cats by generation and by breeding
  // retuns cat id
  function _createKitty(
    uint256 _mumId,
    uint256 _dadId,
    uint256 _generation, //1,2,3..etc
    uint256 _genes, // recipient
    address owner
  ) private returns(uint256) {
    Kitty memory newKitties = Kitty({ // create struct object
      genes: _genes,
      birthTime: uint64(block.timestamp),
      mumId: uint32(_mumId),
      dadId: uint32(_dadId),
      generation: uint16(_generation)
     });

     kitties.push(newKitties); // returns the size of array - 1 for the first cat

     uint256 newKittenId = kitties.length -1; // 0 -1

     emit Birth(owner, newKittenId, _mumId, _dadId, _genes);

     _transfer(address(0), owner, newKittenId); // birth of a cat from 0 (standard)

    return newKittenId; //returns 256 bit integer

  }

  function transferFrom(address from, address to, uint256 tokenId) external override  {
    /// 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

    // Throws unless `msg.sender` is the current owner (I am the "from" address)
    // or the approved address for this NFT (I have approval for this token)
    // or an authorized operator (for "from")
    //require(msg.sender == _from || _approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender));
    require(_owns(from, tokenId)); // Throws if `_from` is not the current owner.
    require(to != address(0)); // Throws if `_to` is the zero address.
    require(tokenId < kitties.length); // Throws if `_tokenId` is not a valid NFT.

    _transfer(msg.sender, to, tokenId);

  }

  // must transfer from address 0
  function _transfer(address from,  address to, uint256 tokenId) internal {

    ownershipTokenCount[to] = ownershipTokenCount[to].add(1);

    kittyIndexToOwner[tokenId] = to;

    // SEMD tokenId # => an address to a number of cats in an array
    ownerToCats[to].push(tokenId);
   
    // decrease token count from person A to person B
    if (from != address(0)) {
      ownershipTokenCount[from] = ownershipTokenCount[from].sub(1);
        _removeTokenIdFromOwner(from, tokenId);
        delete kittyIndexToApproved[tokenId]; 
    }
     
  }

    function _removeTokenIdFromOwner(address owner, uint256 tokenId) internal {
      uint256 lastId = ownerToCats[owner][ownerToCats[owner].length -1];
      for (uint256 i = 0; i < ownerToCats[owner].length; i++) {
        if (ownerToCats[owner][i] == tokenId) {
            ownerToCats[owner][i] = lastId;
            ownerToCats[owner].pop();
        }

      }

  }

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

  function _setApprovalForAll(address operator, bool approved) internal {
    _operatorApprovals[msg.sender][operator] = approved;
  }

  function _toApprove(address approved, uint256 tokenId) internal {
    kittyIndexToApproved[tokenId] = approved;
  }

  // msg.sender and tokenId and check:
  // if kittyIndexToApproved for this _tokenId equals for this _claimant who claims they are approved
  // return true/false
  function _approvedFor(address _claimant, uint256 tokenId) internal view returns(bool) {
    return kittyIndexToApproved[tokenId] == _claimant;
  }

}

IERC721

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 {
    /**
     * @dev Emitted when `tokenId` token is transfered from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

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

    /*
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory tokenName);

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

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


     /* @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) external;

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

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

    /// @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) external view returns (address);

    /// @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) external view returns (bool);
/*
    /// @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;

    /// @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;
*/

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

Hi
You don’t need to import IERC721.sol in the KittyContract. Because the interface is for other contracts (for example the market contract) that needs to know the functionality of KittyContract.

@bjamRez

2 Likes

@REGO350,
thanks - it makes. I thought in the tutorial, it was imported but maybe it wasn’t

2 Likes

Assignment here:

    function transferFrom(address _from, address _to, uint256 _tokenId) public override {
        require(_to != address(0), "Address invalid");
        require(_isApprovedOrOwner(msg.sender, _tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(_from, _to, _tokenId);
    }

    function _isApprovedOrOwner(address _spender, uint256 _tokenId) internal view virtual returns (bool) {
        require(_exists(_tokenId), "ERC721: operator query for nonexistent token");
        require(_ownedBy(msg.sender, _tokenId), "ERC721: Sender does not own this token");
        return (_spender == msg.sender || getApproved(_tokenId) == _spender || isApprovedForAll(msg.sender, _spender));
    }

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

Also, I have an error that states my contract should be marked abstract, thanks for the help

1 Like
transferFrom assignment
 function transferFrom(address _from, address _to, uint256 _tokenId) external override{
    require(kittyindexToOwner[_tokenId]== _from || kittyIndexToApproved[_tokenId] ==_from, "Not the owner of the token");
    require(_to != address(0), "Not possible to transfer to address(0)");
    require(_tokenId< kitties.length, "Token must exist"); 
    
    _transfer(_from, _to, _tokenId);
    }
1 Like

My code below:

function transferFrom(address _from, address _to, uint256 _tokenId) external{
        require (msg.sender == kittyIndexToApproved[_tokenId] || msg.sender == _from);
        require(_tokenId < kitties.length);
        require(_to != address(0));

        _transfer(_from, _to, _tokenId);
    }
    function transferFrom(address _from, address _to, uint256 _tokenId) external override {

        //require address is valid
        require(_to != address(0));

        //make sure _tokenId is valid
        require(_tokenId < totalSupply());

        //checking requirements of approvals
        require((msg.sender == ownerOf(_tokenId)) ||
                (getApproved(_tokenId) == msg.sender) ||
                (isApprovedForAll(ownerOf(_tokenId),msg.sender))
                );
        
        //make transfer
        _transfer(_from,_to,_tokenId);

    }

Here is a tool for flattening Solidity Contracts in VSC - then you can import them into Remix and test your code:

https://marketplace.visualstudio.com/items?itemName=tintinweb.vscode-solidity-flattener