ERC721 Fulfillment – Approval

Hi there,
It is regarding function approve I don’t see anywhere that topic and not sure if my code is correct and got couple of questions as well.

  1. I don’t understand that statement, could anyone explain please:
@dev The zero address indicates there is no approved address.
  1. We need to implement the following to the function approve:
// Throws unless `msg.sender` is the current NFT owner, or an authorized
//  operator of the current owner.

not sure if my code is correct, could anyone confirm please

function approve(address _approved, uint256 _tokenId) external override {
        if (doggieIndexToOwner[_tokenId] == msg.sender) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }
        else if (_operatorApprovals[msg.sender][_approved] == true) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }      
    }

Edited:
I just realised that above code function is wrong but the below maybe too, please can someone comment on that:

function approve(address _approved, uint256 _tokenId) external override {
        if (doggieIndexToOwner[_tokenId] == msg.sender) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }
        else if (_operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }                
    }

full cryptodoggies.sol code

pragma solidity ^0.8.7;

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

contract DoggiesContract is IERC721, Ownable {

    uint public constant CreationLimitGen0 = 30000;
    string public constant override name = "Crypto Doggies";
    string public constant override symbol = "CD";

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

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

    Doggie[] doggies;

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

    uint public gen0Counter;

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

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

        gen0Counter++;

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

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

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

        return newDoggieId;
        
    }

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

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

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

        _transfer(msg.sender, _to, _tokenId);

    }

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

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

        emit Transfer(_from, _to, _tokenId);


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

    }

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external override {
        if (doggieIndexToOwner[_tokenId] == msg.sender) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }
        else if (_operatorApprovals[doggieIndexToOwner[_tokenId]][msg.sender] == true) {
            doggieIndexToApprove[_tokenId] = _approved;
            emit Approved(msg.sender, _approved, _tokenId)
        }                
    }
    //mapping (address => mapping(address => bool)) public _operatorApprovals;
    //mapping (uint256 => address) public doggieIndexToApprove;

    /// @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) {
        require(_tokenId < doggies.length);
        
        return doggieIndexToApprove[_tokenId];

    }

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external {
        require(_operator != msg.sender);

        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool){
        return _operatorApprovals[_owner][_operator];
    }
}

1 Like

Hey @Kamil37, hope you are well.

I might advice you to follow the standards from this ERC20 of openzeppelin, which contains the approve (and _approve) functions that will give you a better idea on how that function should look like.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol

Carlos Z

1 Like

Hi Carlos,

You meant ERC721? and yes make sense when I check there.

thanks for that.

Kamil