Assignment - Safetransfer Implementation

Create safeTransferFrom with and without data parameter.

1 Like
Safetransfer Implementation Assignment
 //safeTransferFrom ERC721 Specification, without data
       function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
           safeTransferFrom(_from, _to, _tokenId, "");
       }
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) public {
        require(_transferChecks(_from, _to, _tokenId));
        
        //Now handled by _transferChecks
        // require(msg.sender == _from || msg.sender == tokenIdToApproved[_tokenId] || isApprovedForAll(_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!");

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

    //Transfers Ownership of a Token
    function transferFrom(address _from, address _to, uint256 _tokenId) external{
        require(_transferChecks(_from, _to, _tokenId));

        //Now handled by _transferChecks
        // require(msg.sender == _from || msg.sender == tokenIdToApproved[_tokenId] || isApprovedForAll(_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

SafeTransferFrom code:

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external override{
        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");

        _safeTransfer(_from,_to,_tokenId, data);

    }


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

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

    }
1 Like

Hi There,

I had a quick conceptual question. I can’t seem to fully understand why in safeTransfer we first execute the transfer and then run the require statement. Shouldn’t it be the other way around following the checks, effects, and interactions model?
I do understand that if the require fails it reverts, but Filip didn’t explain clearly why that order.

Additionally, the onERC721Received function should be implemented in any contract that we want to transfer tokens to. Shouldn’t we have implemented it in our Kittycontract? I assume it would look like this:

	function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4){
		return ERC721Constant;
	}

Where ERC721Constant is bytes4(keccak256(“onERC721Received(address,address,uint256,bytes)”));

Thanks in advance!

Hey @Mariano_Collarte! Normally ERC721 is a estandard with a bit of modifications depending on whart you wanna do with it.In the security side, yes you will have ti set up restrictions for each time you want to use your function!
If you keep with the issue, just pass me your gibhub URL so i can check it.

My code

function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external{
        require(_owns(msg.sender, _tokenId) || kittyIndexToApproved[_tokenId] == msg.sender || _operatorApprovals[_from][msg.sender] == true);
        require(_owns(_from, _tokenId));
        require(_to != address(0));
        require(_tokenId < kitties.length, "the tokenID does not exist");
        _SafeTransferFrom(_from, _to, _tokenId, data);
    }

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
        require(_owns(msg.sender, _tokenId) || kittyIndexToApproved[_tokenId] == msg.sender || _operatorApprovals[_from][msg.sender] == true);
        require(_owns(_from, _tokenId));
        require(_to != address(0));
        require(_tokenId < kitties.length, "the tokenID does not exist");
        _SafeTransferFrom(_from, _to, _tokenId, '0');
    }

I have a little question in those cases I don’t need to put override on my functions , in others I have errors if i don’t put it. I don’t understand why I need to put override if I use exact the same function (used in the interface)

function approve
kittyContract
image
interface
image

function safeTransferFrom
KittyContract
image
interface
image

Hey @camarosan !

These keyword "override " simply allow for more explicit behavior when overriding functions .
So you need to use it when you are naming your functions same as the standard functions. To specify that you are changin the behave.

1 Like

Hi All!

I have a quick question of this video. For me when I would like to migrate the contract I get the following error:

“Kittycontract” is an abstract contract or an interface and cannot be deployed.

  • Import abstractions into the ‘.sol’ file that uses them instead of deploying them separately.
  • Contracts that inherit an abstraction must implement all its method signatures exactly.
  • A contract that only implements part of an inherited abstraction is also considered abstract.

If I am deleting the abstract modification on the contract line, it says that I will need to mark it as abstract. But in that case I am unable to migrate the code.

abstract contract  Kittycontract  is  IERC721, Ownable {

Any tips?

Thank you.

You have to make sure that you input all of the functions in the IERC721 contract.

1 Like

Here is my code:

pragma solidity ^0.8.4;

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

abstract contract Kittycontract  is  IERC721, Ownable  {
    uint256 public constant CREATION_LIMIT_GEN0 = 10;
    string public constant tokenName = "RichKitties";
    string public constant tokenSymbol= "RK";
    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));

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

    Kitty[] kitties;

    mapping (address => uint256) ownershipTokenCount; //maps from owner's address to how many tokens they own
    mapping (uint256 => address) public kittyIndexToOwner; //maps from kitties array index (tokenId?) to address of kitty owner
    
    mapping (uint256 => address) public kittyIndexToApproved;
    mapping (address => mapping(address => bool)) private _operatorApprovals; //msg.sender=>operator=>true/false?
    
    uint256 public gen0Counter;

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


    function createKittyGen0(uint256 _genes) public onlyOwner returns (uint256) {
        require (gen0Counter < CREATION_LIMIT_GEN0, "Creation limit for Kitties reached");
        
        gen0Counter++;

        return _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)
        });
    
        kitties.push(_kitty);
        uint256 newKittenId = kitties.length-1;

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

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

        return newKittenId;
    }

    function getKitty(uint256 _tokenId) public view returns (Kitty memory) {
        return kitties[_tokenId];
    }
    /**
     * @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);

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

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address _owner) external view override returns (uint256 balance) { //external means you can't call it from within the contract
        return ownershipTokenCount[_owner];
    }
    /*
     * @dev Returns the total number of tokens in circulation.
     */
    function totalSupply() public view override returns (uint256) {
        return kitties.length;
    }

    /*
     * @dev Returns the name of the token.
     */
    function name() external pure override returns (string memory) {
        return tokenName;
    }

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

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 _tokenId) external view override returns (address owner) {
        return kittyIndexToOwner[_tokenId]; //what does this mean? Is there just one tokenId? Or multiple?
    }


     /* @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), "Cannot transfer tokenId to zero address");
        require (_to != address(this), "Cannot transfer tokenId to contract address");
        require (_owns(msg.sender,_tokenId), "Cannot transfer tokenId if you don't own it");

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

    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        ownershipTokenCount[_to]++;
        kittyIndexToOwner[_tokenId] = _to; //change ownership in the actual mapping

        if (_from != address(0)){
            ownershipTokenCount[_from]--;
            delete kittyIndexToApproved[_tokenId];
            }
        //emit the transfer event
        emit Transfer(msg.sender, _to, _tokenId);
    }
    

    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return kittyIndexToOwner[_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 {
        require (_owns(msg.sender,_tokenId)|| _operatorApprovals[msg.sender][_approved]==true, "Cannot set approval for this tokenId if you don't own it or are approved for it");

        _approve(_approved, _tokenId);

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

    function _approve(address _approved, uint256 _tokenId) private {
        kittyIndexToApproved[_tokenId] = _approved;
    }

    /// @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 override {
        require(_operator != msg.sender, "Cannot set asset owner as operator");

        _operatorApprovals[msg.sender][_operator]=_approved;
        //do I have to loop through all of this msg.sender's tokens and approve for each of them?
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /// @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 override view returns (address) {
        require(kittyIndexToOwner[_tokenId] != address(0) ); //check if it's a valid NFT, i.e. if it has an owner
            
        return kittyIndexToApproved[_tokenId]; //will this return the zero address if there is none?
    }

    /// @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) public override view returns (bool) {
        
        return _operatorApprovals[_owner][_operator];
    }

    /// @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 override {
        require(_owns(msg.sender, _tokenId)|| approvedFor(msg.sender,_tokenId)|| isApprovedForAll(_from,msg.sender));
        require(_from == kittyIndexToOwner[_tokenId]);
        require(_to != address(0));
        require(_tokenId < kitties.length);

        _transfer(_from, _to, _tokenId);
    }

        /// @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) override external {
        require(_owns(msg.sender, _tokenId)|| approvedFor(msg.sender,_tokenId)|| isApprovedForAll(_from,msg.sender));
        require(_from == kittyIndexToOwner[_tokenId]);
        require(_to != address(0));
        require(_tokenId < kitties.length);

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

    /// @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) override external {
        require(_owns(msg.sender, _tokenId)|| approvedFor(msg.sender,_tokenId)|| isApprovedForAll(_from,msg.sender));
        require(_from == kittyIndexToOwner[_tokenId]);
        require(_to != address(0));
        require(_tokenId < kitties.length);

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

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

    function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
        if( !_isContract(_to) ){ //if it isn't a contract, we're sending to a wallet, and no further checks required
            return true;
        }
        
        //have to call the _to contract, call onERC721Received
        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);

        //check return value is equal to bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
        return returnData == MAGIC_ERC721_RECEIVED;
    }

    function _isContract(address _to) view internal returns (bool) {
        uint32 size;
        assembly{
            size := extcodesize(_to)
        }
        return size > 0; //if code size > 0, then it is a contract (as opposed to a wallet)
    }
}
1 Like

Hi Mariano, I have the same question as you around the order of execute the transfer versus where the require statement is - have you got any further insight into this? Thanks.

Hi Riki, I’m getting the same thing - when I try to compile it tells me that I have to make the contract abstract. I haven’t tried to deploy the contract yet, so I’ll see if I get the same error as you.
I think this has something to do with the solidity version that one is working in. When Filip did this video they were working in 0.5.12 or something, but I’m not trying to implement this in 0.8.4, which could be why this ‘abstract’ issue is now coming up?
Let me know if you’ve been able to make any further progress on this, thanks!
Richard

So I am having problems implementing the _isApprovedOrOwner function. When I input the return statement, I get an error for the approve. But when I change it to the approvedFor and create that corresponding function, I get an error that my contract should be abstract which completely destroys the contract. What shall I do? I am also still not getting the contract instance automatically, but I am getting the instance when creating the order and it shows that my metamask is connected. I will show the .sol on here first and then the index.js for Web3 //commented out errors.
If you would like I can post on GitHub for you to view the whole project
Code:

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

 import "./IERC721.sol";
 import "./IERC721Receiver.sol";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";


 contract SupplyChainProtocol is IERC721 {
     using SafeMath for uint256;
    string public name = "SupplyChainProtocol";
    string public symbol= "SCP";
    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    /**
     *  bytes4(keccak256('supportsInterface(bytes4)'));
    */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    
    event OrderMade(address owner,
     uint256 OrderVolumeId,
      uint256 cost,
      string productName,
      string description,
      uint256 leadTime_in_days
    );
  struct Order{
      uint256 id;
      uint64 cost;
      string productName;
      string description;
      uint64 leadTime_in_days;
    }
    Order[] orderVolume;
   
   
    mapping(address => uint256) private balances;
    mapping(address => uint256) orders;
    mapping(address => uint256) OrderIncites;
    mapping(uint256 => address) orderIdMapping;
    mapping(uint256 => address) public orderIndexToApproved;
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    function _createOrder(uint256 _ID,
        uint64 _cost,
        string memory _productName,
        string memory _description,
        uint64 _leadTime_in_days,
        address _owner
       ) external returns (uint256) {
         Order memory _order = Order({
             id:_ID,
             cost: uint32(_cost),
             productName: string(_productName),
             description: string (_description),
             leadTime_in_days: uint32(_leadTime_in_days)
             });
             
          orderVolume.push(_order);
          uint256 newOrderVolumeId=orderVolume.length - 1;
          emit OrderMade(_owner, newOrderVolumeId, _cost, _productName, _description, _leadTime_in_days);
          _transfer(address(0), _owner, newOrderVolumeId);
          return newOrderVolumeId;  

    }
  
    function getOrder(uint256 _id) external view returns(
        uint256 id,
        uint256 cost,
        string memory productName,
        string memory description,
        uint256 leadTime_in_days
    ) {
        return (
            orderVolume[_id].id,
            orderVolume[_id].cost,
            orderVolume[_id].productName,
            orderVolume[_id].description,
            orderVolume[_id].leadTime_in_days
        );
    } 
 
     
    function totalSupply() public view returns (uint256 total){
        return orderVolume.length;
    }
 
    function balanceOf(address owner) public view virtual override returns (uint256 balance){
        return OrderIncites[owner];
    }
    function ownerOf(uint256 tokenId) public view virtual override returns (address owner){
        return orderIdMapping[tokenId];
        
    }
 
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4){}

    
    function transfer(address from, address to, uint256 tokenId) external{
        require(address(to) != address(0));
        require(address(from) != address(0));
        require(orderIdMapping[tokenId]==msg.sender);
        emit Transfer(msg.sender, to, tokenId);
    }
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        balances[_to]++;
        _to=orderIdMapping[_tokenId];
        if(_from != address(0)) {
            balances[_from]--;
            delete orderIndexToApproved[_tokenId];
        }
        emit Transfer(_from, _to, _tokenId);

    }
    function _owns(address _claimaint, uint256 _tokenId) internal view returns(bool){
        return orderIdMapping[_tokenId] == _claimaint;
    }
     function approve(address _to, uint256 _tokenId) external override {
        require(_owns(msg.sender, _tokenId));
        _approve(_tokenId, _to);
        emit Approval(msg.sender, _to, _tokenId);
    }
    
     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 override returns (address){
        require(_tokenId < orderVolume.length);//check that the token exists
        return orderIndexToApproved[_tokenId];
    }

    function isApprovedForAll(address _owner, address _operator) external override view returns (bool){
        //returns the mapping status for these inputs
        return _operatorApprovals[_owner][_operator];
    } 
    function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
        if (!_isContract(_to)) {
            return true;
        }

        // Call onERC721Received in the _to contract
        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _to, _tokenId, _data);
        return returnData == MAGIC_ERC721_RECEIVED;
    }
     function _isContract(address _to) internal view returns (bool) {
        uint32 size;
        assembly {
            size := extcodesize(_to)
        }
        return size > 0;
    }
    function supportsInterface(bytes4 _interfaceId) virtual override external pure returns (bool) {
        return (_interfaceId == _INTERFACE_ID_ERC721 || _interfaceId == _INTERFACE_ID_ERC165);
    }
    
    function safeTransferFrom(address _from, address  _to, uint256 _tokenId, bytes memory _data) external override {
        require(_isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
        _safeTransfer(_from, _to, _tokenId, _data);

    }
     function transferFrom(address _from, address _to, uint256 _tokenId) external override{
        require(_isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
        _transfer(_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 _safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public {
        _safeTransferFrom(_from, _to, _tokenId, data);

    }

    function _approve(uint256 _tokenId, address _approved) internal {
        orderIndexToApproved[_tokenId] = _approved;
    }
    function _isApprovedOrOwner(address spender, address _from, address _to, uint256 _tokenId) internal view {
        require(_tokenId < orderVolume.length);
        require(_to != address(0));
        require(_owns(_from, _tokenId));
        //Error at approve(spender, _tokenId)
        return (spender == _from || approve(spender, _tokenId) || this.isApprovedForAll(_from) );
    }
  
   
    
 }    

Web3 index.js Code:


var web3 = new Web3(Web3.givenProvider);

let order; 
let accounts;
let instance;
let user;
let abi;
let contractAddress= "0xDe34Ee16b231EFFeBe073E3BA587E06Fa55d85B1";
$(document).ready(function(){
  window.ethereum.enable().then(function(accounts){
    instance = new web3.eth.Contract(abi, contractAddress, {from: accounts[0]});
    user = accounts[0];
    console.log(instance);
    instance.events.OrderMade().on('data', function(event){
      console.log(event);
      let owner = event.returnValues.owner;
      console.log(owner);
      let orderId = event.returnValues.orderVolumeId;
      console.log(orderId);
      let cost = event.returnValues.cost;
      console.log(cost);
      let productName = event.returnValues.productName;
      console.log(productName);
      let description = event.returnValues.description;
      console.log(description);
      let leadTime_in_days = event.returnValues.leadTime_in_days;
      console.log(leadTime_in_days);
  
      $("#OrderMade").css("display", "block");
      $("#OrderMade").text( "OrderId: " + orderId + 
                             "Owner: " + owner + 
                             "cost: "+ cost + 
                             "ProductName: " + productName +
                             "description: " + description +
                             "leadTime: " + leadTime_in_days
                              );
  
  })
  .on('error', console.error)
  })
})
const _createOrder = async () => {
  order = await window.ethereum.request({method: 'eth_requestAccounts'}).catch((err) => {
      console.log(err.code);
  })
  console.log(order);
} 
  

My WORKING CONTRACT:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma experimental ABIEncoderV2;
 import "./IERC721.sol";
 import "./IERC721Receiver.sol";
 


 contract SupplyChainProtocol is IERC721 {
  
    string public override constant name = "SupplyChainProtocol";
    string public override constant symbol= "SCP";
    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
   
    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    /**
     *  bytes4(keccak256('supportsInterface(bytes4)'));
    */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
    
  struct Order{
      uint256 id;
      uint64 cost;
      string productName;
      string description;
      uint64 leadTime_in_days;
    }
    Order[] private orderVolume;
    
   
   
    mapping(address => uint256) private balances;
    mapping(address => uint256) private OrderIncites;
    mapping(uint256 => address) private orderIdMapping;
    mapping(uint256 => address) private orderIndexToApproved;
    mapping(address => mapping(address => bool)) private _operatorApprovals;
   event OrderMade(address owner,
     uint256 OrderVolumeId,
      uint256 cost,
      string productName,
      string description,
      uint256 leadTime_in_days
    );
    function _createOrder(uint256 _ID,
        uint64 _cost,
        string memory _productName,
        string memory _description,
        uint64 _leadTime_in_days,
        address _owner
       ) private returns (uint256) {
           
         Order memory _order = Order({
             id:_ID,
             cost: uint32(_cost),
             productName: string(_productName),
             description: string (_description),
             leadTime_in_days: uint32(_leadTime_in_days)
             });
             
          orderVolume.push(_order);
          uint256 newOrderVolumeId=orderVolume.length - 1;
          emit OrderMade(_owner, newOrderVolumeId, _cost, _productName, _description, _leadTime_in_days);
          _transfer(address(0), _owner, newOrderVolumeId);
          return newOrderVolumeId;  

    }
  
    function getOrder(uint256 _id) external view returns(
        uint256 id,
        uint256 cost,
        string memory productName,
        string memory description,
        uint256 leadTime_in_days
    ) {
        return (
            orderVolume[_id].id,
            orderVolume[_id].cost,
            orderVolume[_id].productName,
            orderVolume[_id].description,
            orderVolume[_id].leadTime_in_days
        );
    } 
 
     
    function totalSupply() external view override returns (uint256 total){
        return orderVolume.length;
    }
 
    function balanceOf(address owner) external view override returns (uint256 balance){
        return OrderIncites[owner];
    }
    function ownerOf(uint256 tokenId) external view override returns (address owner){
        return orderIdMapping[tokenId];
        
    }
 

    
    function transfer( address to, uint256 tokenId) external override{
        require(address(to) != address(0));
        require(to != address(this));
        require(_owns(msg.sender, tokenId));
        _transfer(msg.sender,to,tokenId);
    }
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        balances[_to]++;
        _to=orderIdMapping[_tokenId];
        if(_from != address(0)) {
            balances[_from]--;
            delete orderIndexToApproved[_tokenId];
        }
        emit Transfer(_from, _to, _tokenId);

    }
    function _owns(address _claimant, uint256 _tokenId) internal view returns(bool){
        return orderIdMapping[_tokenId] == _claimant;
    }
     function approve(address _approved, uint256 _tokenId)public override payable {
        require(_approved != address(0));
        require(_owns(msg.sender, _tokenId));
        _approve(_approved, _tokenId);
        emit Approval(orderIdMapping[_tokenId], _approved, _tokenId);
    }
    
     function setApprovalForAll(address operator, bool _approved) external override{ 
        require(operator != address(0));
        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 override returns (address){
        require(_tokenId < orderVolume.length);//check that the token exists
        return orderIndexToApproved[_tokenId];
    }

    function isApprovedForAll(address _owner, address _operator) external override view returns (bool){
        //returns the mapping status for these inputs
        return _operatorApprovals[_owner][_operator];
    } 
    function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
        if (!_isContract(_to)) {
            return true;
        }

        // Call onERC721Received in the _to contract
        bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
        return returnData == MAGIC_ERC721_RECEIVED;
    }
     function _isContract(address _to) internal view returns (bool) {
        uint32 size;
        assembly {
            size := extcodesize(_to)
        }
        return size > 0;
    }
    
    
    function safeTransferFrom(address _from, address  _to, uint256 _tokenId, bytes memory data) public override payable {
        require(_isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
        _safeTransfer(_from, _to, _tokenId, data);

    }
     function transferFrom(address _from, address _to, uint256 _tokenId) external override payable{
        require(_to != address(0));
        address owner = orderIdMapping[_tokenId];
        require(_to != owner); // not sending to the owner
        require(msg.sender == _from || approvedFor(msg.sender, _tokenId) || this.isApprovedForAll(owner, msg.sender)); // currenct address is the owner of the token
        require(_owns(_from, _tokenId), "From is not the owner of the token");
        require(_tokenId < orderVolume.length, "Token ID is not valid");

        _transfer(_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 safeTransferFrom(address _from, address _to, uint256 _tokenId) external override payable {
        safeTransferFrom(_from, _to, _tokenId,"");

    }

    function _approve(address _approved, uint256 _tokenId) private {
        orderIndexToApproved[_tokenId] = _approved;
    }
    function approvedFor(address claimant, uint256 tokenId) internal view returns(bool){
        return orderIndexToApproved[tokenId] == claimant;
    }

    function _isApprovedOrOwner(address spender, address _from, address _to, uint256 _tokenId) private view returns(bool) {
        require(_tokenId < orderVolume.length);
        require(_to != address(0));
        address owner = orderIdMapping[_tokenId];
        require(_to != owner);
        require(_owns(_from, _tokenId));
        //Error at approve(spender, _tokenId)
        return (spender == _from || approvedFor(spender, _tokenId) || this.isApprovedForAll(owner, spender) );
    }
  
   
    
 }    


 
   

Hey @Riki Should not be abstract since you gonna use the functions.

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public and private concrete methods.

Hi @RichJamo

Probably what you say is right but I have made a bit of pause to the D’app programming course as I try to make my first own project. Will update you when I will return to it (probably next week).

1 Like

@AdamFortuna, @Bhujanga
Hello Devs, I am getting an error when I use an empty " " string - here is my code

function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
    require(msg.sender == from || _approvedFor(from, tokenId) || isApprovedForAll(from, msg.sender));
    require(_owns(from, tokenId), "Throws if `_from` is not the current owner.");
    require(to != address(0), "to cannot be the zero address" );
    require(tokenId < kitties.length, "not a valid NFT" );

    _safeTransfer(msg.sender, to, tokenId, data);

  }

  
  function safeTransferFrom(address from, address to, uint256 tokenId) external {
    //string memory noData = "";
    _safeTransfer(msg.sender, to, tokenId, "");
  }

  function _safeTransfer(address from, address to, uint256 tokenId, bytes calldata data) internal {
    require(_checkERC721Support(from, to, tokenId, data));

    _transfer(from, to, tokenId);
  }

data_string_error

Instead of calldata, why not memory?

  • memory - variable is in memory and it exists while a function is being called
  • calldata - special data location that contains function arguments, only available for external function

Carlos Z

1 Like

@thecil

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

interface IERC721Receiver {
  function onERC721Received(address from, address to, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

do you mean here in the above function?

and why did it work on Filip’s tutorial - Solidity v 0.5 …something. We are now using 08.4

I mean, you are rewriting yourself functions that are already settle by presets like Openzeppelin, although the version of solidity has been updated, some methods are still not applied to presets.

I might suggest you to use the same function structure than openzeppelin erc721 contract (and ierc721)

https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721

Carlos Z

1 Like