Cryptokitty NFT marketplace course implicitly convertible error

Hi all,
I’m going through the Cryptokitty NFT marketplace course and trying to compile a contract, and I’ve got into the following error, which I’m not sure how to solve it even after trying to google it. I have an idea of what it meant, I think truffle is telling me I need to explicitly convert the struct value from uint16 to uint256 instead of implicitly doing so, but I’m not sure how to do it.

below is the the error message, followed by my smart contract:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./IERC721.sol";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";

// Kittycontract inheriting from IERC721
// I in IERC721 stands for interface, 
// it's pretty much the functions header file for solidity
// still need to implement the functions before deploying the contract
// in order to make the smart contract to work in my own logic
contract Kittycontract is IERC721, Ownable{

    //state variables
    uint256 public constant CREATION_LIMIT_GEN0 = 10;
    string public constant name = "MeowKitties";
    string public constant symbol = "MK";


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

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

    Kitty[] kitties;

    mapping (uint256 => address) public kittyIndexToOwner; //takes kitty ID and return owner address
    mapping (address => uint256) ownershipTokenCount; //takes an address and return how many kitty that address own

    uint256 public gen0Counter;

    //function for retuning the characteris of the kitty
    function getKitty(uint256 _id) external view returns(
        uint256 genes,
        uint64 birthTime,
        uint32 mumId,
        uint32 dadId,
        uint16 generation
    ){
        //pointers, can use memory instead of storage, but memory takes more space
        Kitty storage kitty = kitties[_id];

        //convert to uint256, because its easier to read in frontend
        birthTime = uint256(kitty.birthTime);
        mumId = uint256(kitty.mumId);
        dadId = uint256(kitty.dadId);
        generation = uint256(kitty.generation);
        genes = kitty.genes;
    }

    //create generation 0 kitty function
    //onlyOwner - means only the owner of this contract can call this function
    function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256){
        require(gen0Counter < CREATION_LIMIT_GEN0); //only can create a certain amount of Gen0 kitties

        gen0Counter++;

        //Gen0 have no owners they are own by the contract
        return _createKitty(0, 0, 0, _genes, msg.sender);
    }

    //to be use for breeding kitties
    //_ before function name, general practice means internal function.
    function _createKitty(
        uint256 _mumId,
        uint256 _dadId,
        uint256 _generation,
        uint256 _genes,
        address _owner //the recipten of this newly minted kitty
    ) private returns (uint256){ //returns new minted kitty ID
        //creating new kitty struct
        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(block.timestamp), //now means seconds since 1/1/1970 get from Eth network
            mumId: uint32(_mumId), //converting a uint256 integer to uint32 integer
            dadId: uint32(_dadId),
            generation: uint16(_generation)
        });
        
        kitties.push(_kitty);
        uint256 newKittenId = kitties.length -1;

        //emitting the "Birth" even
        emit Birth(_owner, newKittenId, _mumId, _dadId, _genes);

        //_owner, meaning not transfer to another account, but rahter meant its 
        // breeding a new kitty
        _transfer(address(0), _owner, newKittenId);

        return newKittenId;

    }

    //external - can ONLY be call the function outside of this contract by other contract, 
    //              usually cheaper in gas to call external
    //view - read only, can't change or mainupulate variables in this contract
    //returns - expects to return something, in this case returns a uin256 value
    function balanceOf(address owner) external view returns (uint256 balance){
        return ownershipTokenCount[owner];
    }

    //public - everyone can call this function
    function totalSupply() public view returns (uint){
        return kitties.length; //returns the length of the Kitty (struct) array
    }

   //returns an address 
    function ownerOf(uint256 _tokenId) external view returns (address) {
        return kittyIndexToOwner[_tokenId];
    }

    function transfer(address _to, uint256 _tokenId) external{
        require(_to != address(0)); //reciever address can't be address 0 (caller of this function) before allow to continue
        require(_to != address(this)); //reciever address can't be THIS contract before allow to continue
        require(_owns(msg.sender, _tokenId)); //sender must own that kitty he wants to send before allow to continue
        
        //need to pass all the previous require first before calling below function
        _transfer(msg.sender, _to, _tokenId);
    }

    //internal - this function can ONLY be access and view by this contract
    //this function is separated from the transfer function above so it can be reused,
    //for example use it to mint new kitties 
    function _transfer(address _from, address _to, uint256 _tokenId) internal{
        ownershipTokenCount[_to]++;

        kittyIndexToOwner[_tokenId] = _to; //set the owner of the kitty being transfer to the reciever

        //address(0) is the address of the person calling this function,
        //thus only deduct the number of kitties owned by caller, if trasnfer out, not minting new kitty
        if(_from != address(0)){
            ownershipTokenCount[_from]--; // deduct number of kitties owned from sender after transfer
        }

        //Emit the transfer event. Calling emit is part of ERC721 standard.
        emit Transfer(_from, _to, _tokenId);
    }

    //this function returns a bool, true if _claimant indeed owns the kitty he claims he owns
    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool){
        return kittyIndexToOwner[_tokenId] == _claimant; 
    }

}

Hi @Philip_Wong

I this part of the code you defined the returns type of generation as uint16, hence generation = uint256(kitty.generation) is throwing an error. You can update the return type of generation as uint256 to fix the error or else you can just assign generation = kitty.generation without converting to uint256

function getKitty(uint256 _id) external view returns(
        uint256 genes,
        uint64 birthTime,
        uint32 mumId,
        uint32 dadId,
        uint16 generation
    ){
        //pointers, can use memory instead of storage, but memory takes more space
        Kitty storage kitty = kitties[_id];

        //convert to uint256, because its easier to read in frontend
        birthTime = uint256(kitty.birthTime);
        mumId = uint256(kitty.mumId);
        dadId = uint256(kitty.dadId);
        generation = uint256(kitty.generation);
        genes = kitty.genes;
    }

Hope this helps.

Hi John,

I understand what you’re say, however in the course video Filip defined the generation in the struct as

uint16 generation

thus leads to the conversion of uint16 to uint256 as shown in my question. In the video, Filip want to convert a unit16 to a uint256 value such that it is easier to display on the frontend. Thus the code,

generation = uint256(kitty.generation)

I understand that if I go to the struct and change it to uint256, it will solve the problem without the need of calling uint256(generation), however I would like to know, is there a way to explicitly convert uint16 to uint256 as stated by the typeError message, which I’ve posted in the question?

if there isn’t a solution to it now, I’m happy to just change the struct to uint256, but if there’s a way to explicitly convert it, I’d like to know how to do it, because I tried Googling it but couldn’t find a solution.

thanks

1 Like

The error says uint256 is not implicitly convertible to expected type unit16. This is because you are trying to assign a uint256 to uint16

generation = uint256(kitty.generation);
// return type generation is defined as type uint16 in "getKitty" function 
// kitty.generation is of type uint16 and converted to uint256 using "uint256()"

The conversion here is correct. It is just that you are trying to assign the converted type to the wrong data type.

Hope this is clear now😃

2 Likes

right, I think I got it. so instead of trying to put a uint256 value into a uint16 variable. I should change the generation variable in getKitty to uint256.

2 Likes

Hello @JohnVersus
pleased to see your issue have you got help or not ? otherwise you can visit Dinar Guru