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 {
}
}