1

I am building a program with an ERC 721 token. My code runs without using the standard components provided by Open Zeppelin IERC721. Now I want to adapt it to the ERC 721 standard. Unfortunately the transfer function does not work. I need to see more example codes for ERC721 contracts to understand the logic of what to code. Do you know where I can find more examples?

Otherwise, maybe you directly spot the problem? I built my code and implemented the requested functions for the ERC token in the end so the compiler accepts. In my code I only want to use the safeTransferFrom function, but the transfer is not working. I would be thankful for any advice.

This is my code so far:

// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.6.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721Receiver.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/introspection/ERC165.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol";

contract Parknet is IERC721, ERC165  {

    
 
    
   //Save address of holder of token
    mapping (uint256 => address payable) addressTokenHolder;
    
   //Save address of creator of token
    mapping (uint256 => address payable) addressTokenCreator; 
    
   //Save name of the token 
    mapping (uint256 => string) tokenname;
    
   //Create token register 
    uint256[] tokens; 
    
    //Input from frontend: ID and token name (Later create ID by random number)
    function createNewParkspot(uint256 _tokenId, string memory _name) public isUnique(_tokenId){
    require(_tokenId != 0, "Token ID can not be 0");
    
    tokens.push(_tokenId) ;
        
     addressTokenCreator[_tokenId] = msg.sender;
     
     addressTokenHolder[_tokenId] = msg.sender;

     tokenname[_tokenId] = _name;
     
     //Save numbers of parkspots per address
     ownertokenCount[msg.sender] = (ownertokenCount[msg.sender] + 1);

     
    }
    //Show all parkspots
     function getAllIDs() public view returns (uint256[] memory) {
    return tokens;
    }
   
    //Count all parkspots
    function countParkspots() view public returns (uint) {
        return tokens.length;
    }
    //Get current holder of parkspot: Either creator or parker
    function getAddress(uint256 _tokenId) view public returns (address) {
        return (addressTokenHolder[_tokenId]);
    }   
   

    //Recieve data behind the ID: Name or in the future address, number, GPS location,...
    function getNamefromTOKENID(uint256 _tokenId) view public returns (string memory) {
    return (tokenname[_tokenId]);
    }
 
 
 
 
 
 //Create parkingfee register
    mapping (uint256 => uint) parkingfee;
    
// Creator can set fee per time of parking spot
    function SetParkingFee(uint256 _tokenId, uint _fee) public {
// Only creator can set the price     
     require (msg.sender == addressTokenCreator[_tokenId]);
  //Check if token exists
 require((_exists(_tokenId)), "Token does not exist");   
     
//input in fee/min but calculated as fee per second for technical processing 
    parkingfee[_tokenId] = _fee/60;
 
    }
    
    //See price of parkspot by entering ID
    function GetParkingFee(uint256 _tokenId) view public returns (uint) {
        return (parkingfee[_tokenId]*60);
    }
 
 


//Store start park time
mapping (uint256 => uint) parktime;

 //Store the balances of participants
 mapping(address => uint) public balances;

function StartParking (uint256 _tokenId) external payable {
 //First a deposit is required
 //Ensure balance of relevant height
 //Check if token exists
 require((_exists(_tokenId)), "Token does not exist");
 
    if(msg.value < 1000000000000000000){
        require(msg.value < 1000000000000000000, "Deposit of min. 1 ether required");
        revert();
    } 
 
  //Add deposit to balance of sender 
  else {  balances[msg.sender] += msg.value;
 
    //Transfer token ownership from creator to parker                                                         here safe transfer from 
//     addressTokenHolder[_tokenId] = msg.sender;
     
safeTransferFrom(addressTokenCreator[_tokenId], msg.sender, _tokenId);    
     
    //Save time of parking start
     parktime[_tokenId] = block.timestamp;
  
    }
}   
    
    
mapping (uint256 => uint) parkingduration;

mapping (uint256 => uint) bill;
  
  
   function EndParking (uint256 _tokenId) public payable{
      
      //Check if token exists
         require((_exists(_tokenId)), "Token does not exist");
      // Only parker can end his parking    
         require (msg.sender == addressTokenHolder[_tokenId]);  
      
       //Return park token to creator                                                                                  here safe transfer from
//       addressTokenHolder[_tokenId] = addressTokenCreator[_tokenId];
       
       
       safeTransferFrom(msg.sender, addressTokenCreator[_tokenId], _tokenId); 
       
       //Return remaining money from the deposit
       parkingduration[_tokenId] = block.timestamp - parktime[_tokenId];
       
       bill[_tokenId] = (parkingduration[_tokenId]*parkingfee[_tokenId]);
       
       //payment function included:

      //ensure deposit is high enough to pay the bill
    if (balances[msg.sender] >= bill[_tokenId]) {
     //Send fee to the creator of the parking spot    
     addressTokenCreator[_tokenId].transfer(bill[_tokenId]);
     //Subtract payment amount from deposit of parker
     balances[msg.sender] = (balances[msg.sender] - bill[_tokenId]);
     //Return remaining deposit to parker
     msg.sender.transfer(balances[msg.sender]);
    }
    //If there is not enough deposit left, the parking token returns from the parker to the creator 
    else { require(balances[msg.sender] >= bill[_tokenId], "Deposit not sufficient. Use deposit function to refill deposit and end parking again");
        }
      

   } 
    
  

 
 
 
 
 
//Receipt and control functions
 
 //For control only
    function ShowlastBill (uint256 _tokenId) view public returns (uint) {
        return bill[_tokenId];
    }
 
//Show parking duration               For control only 
  function ShowDuration (uint256 _tokenId) view public returns (uint) {
        return parkingduration[_tokenId];
    }
 
 
 //Show user balance          For control only
    function ShowDeposit () view public returns (uint) {
        return (balances[msg.sender]);
    } 

 //Show balance of the contract        For control only
 function Contractbalance() external view returns (uint){
     return address(this).balance;
 }
 
 
 
 
   function burn(uint256 _tokenId) external {
        // Error handling to check for certan conditions before transfer
        // if checks fail, all state changes from this call reverted
        require(msg.sender != address(0));
        require((_exists(_tokenId)), "Token does not exist");
        require (msg.sender == addressTokenCreator[_tokenId], "Only creator can delete token");
        
        delete tokens[_tokenId];
    
    //Save numbers of parkspots per address
     ownertokenCount[msg.sender] = (ownertokenCount[msg.sender] - 1);

    }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 //Emergency functions 
 
 
  //deposit an amount to pay for the parking spot
 function deposit() external payable {
   
   //Ensure balance of relevant height
    if(msg.value < 1 ether){
        require(msg.value < 1000000000000000000, "Deposit exceeding 1 ether required");
        revert();
    } 
  //Add deposit to balance of sender 
  else {  balances[msg.sender] += msg.value;
   //Show balance of sender
   
 } }
 
 //return the remaining funds of the deposit in case of error of normal code
 function returndeposit() public payable {
     msg.sender.transfer(balances[msg.sender]);
 }
 
 
 
 
 
 
//Technical background functions

  // Check if parkspotId exists
    function _exists(uint256 _tokenId) internal view returns (bool) {
        address owner = addressTokenCreator[_tokenId];
        return owner != address(0);
    }
    
    
  
    // Check if ID is unique and doesn't exist yet
    modifier isUnique(uint256 _tokenId) {
        bool result = true;
         
            if (addressTokenCreator[_tokenId] != address(0)) {
            
                result = false;
            }
        
        require(result, "Token with such an ID already exists.");
        _;
    }  
    



//ERC721 functions
/**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */

 function safeTransferFrom(address from, address payable to, uint256 _tokenId) public {
        require(isApproved(msg.sender));
        // solium-disable-next-line arg-overflow
        this.safeTransferFrom(from, to, _tokenId, "");
        addressTokenHolder[_tokenId] = to;
    }



 // Mapping from token ID to approve address
    mapping(uint256 => address) tokenApprovals;
    
  function approve(address _to, uint256 _tokenId) external override {
     
        tokenApprovals[_tokenId] = _to;
        emit Approval(msg.sender, _to, _tokenId);
    }



 function isApproved(address spender)
        internal
        view
        returns (bool)
    {
        address owner = msg.sender;
       
        return (spender == owner);
    }

// Return approved address for specific Pizza
    function getApproved(uint256 _tokenId) override external view returns (address operator)  {
        require(_exists(_tokenId));
        return tokenApprovals[_tokenId];
    }


  /*
     * Sets or unsets the approval of a given operator
     * An operator is allowed to transfer all tokens of the sender on their behalf
     */
     
      // You can nest mappings, this example maps owner to operator approvals
    mapping(address => mapping(address => bool)) private operatorApprovals;
    
    function setApprovalForAll(address to, bool approved) public override {
        
        operatorApprovals[msg.sender][to] = approved;
        emit ApprovalForAll(msg.sender, to, approved);
    }

// Tells whether an operator is approved by a given owner
    function isApprovedForAll(address owner, address operator) override public view returns (bool)
    {
        return operatorApprovals[owner][operator];
    }









 // Returns owner of the Token found by id
    function ownerOf(uint256 _tokenId) override public view returns (address _owner) {
        return (addressTokenHolder[_tokenId]);
    }



   // Mapping from owner to number of owned token
    mapping(address => uint256) public ownertokenCount;
    // Returns count of tokens by address
    function balanceOf(address _owner) override public view returns (uint256 _balance) {
        return ownertokenCount[_owner];
    }

// Transfer token and ownership to other wallet
    function transferFrom(address _from, address _to, uint256 _tokenId) override public {
   
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) override external {
}   
}
2
  • It is unclear what the exact problem is. "In my code I only want to use the safeTransferFrom function" - What's stopping you??? Commented Jun 25, 2020 at 14:27
  • Thanks for the advice. The problem is, that the function does not work. I can run the startParking function, but when I run the stopParking function it failes. Via the remix debugging I saw that it is likely the transfer has not been completed in the startParking function. But I don`t know why
    – nie
    Commented Jun 25, 2020 at 14:40

2 Answers 2

2

Here are a few links that might help you find examples:

1

Read to the bottom of the ERC-721 standard.

Four reference implementations are cited.

I think the first one is the best place to start 0xcert ERC-721 https://github.com/0xcert/ethereum-erc721

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.