ERC4907 compile issue in "function _beforeTokenTransfer" - solidity

I am working on an NFT Marketplace creation task with Truffle. For that, I am using an ERC4907 smart contract and trying to compile it. Following is my ERC4907 code.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./IERC4907.sol";
contract ERC4907 is ERC721URIStorage, IERC4907 {
struct UserInfo {
address user; // address of user role
uint64 expires; // unix timestamp, user expires
}
mapping(uint256 => UserInfo) internal _users;
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}
/// #notice set the user and expires of a NFT
/// #dev The zero address indicates there is no user
/// Throws if `tokenId` is not valid NFT
/// #param user The new user of the NFT
/// #param expires UNIX timestamp, The new user could use the NFT before expires
function setUser(
uint256 tokenId,
address user,
uint64 expires
) public virtual override {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"ERC721: transfer caller is not owner nor approved"
);
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
emit UpdateUser(tokenId, user, expires);
}
/// #notice Get the user address of an NFT
/// #dev The zero address indicates that there is no user or the user is expired
/// #param tokenId The NFT to get the user address for
/// #return The user address for this NFT
function userOf(uint256 tokenId)
public
view
virtual
override
returns (address)
{
if (uint256(_users[tokenId].expires) >= block.timestamp) {
return _users[tokenId].user;
} else {
return address(0);
}
}
/// #notice Get the user expires of an NFT
/// #dev The zero value indicates that there is no user
/// #param tokenId The NFT to get the user expires for
/// #return The user expires for this NFT
function userExpires(uint256 tokenId)
public
view
virtual
override
returns (uint256)
{
return _users[tokenId].expires;
}
/// #dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override
returns (bool)
{
return
interfaceId == type(IERC4907).interfaceId ||
super.supportsInterface(interfaceId);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (from != to && _users[tokenId].user != address(0)) {
delete _users[tokenId];
emit UpdateUser(tokenId, address(0), 0);
}
}
}
I am getting the following error when trying to compile it. Error is on the "_beforeTokenTransfer" function.
CompileError: TypeError: Wrong argument count for function call: 3 arguments given but expected 4.
--> project:/contracts/ERC4907.sol:87:5:
|
87 | super._beforeTokenTransfer(from, to, tokenId);
|
I am using following versions
Truffle v5.6.8 (core: 5.6.8)
Ganache v7.5.0
Solidity v0.5.16 (solc-js)
Node v16.17.1
Web3.js v1.7.4
I have checked with the reference implementation on https://eips.ethereum.org/EIPS/eip-4907 . But it is still the same code.
Can someone please show me what I have mistaken here? Thank You
EDIT
As answered by #Muhammad Hassan, it was identified that there was a breaking change in the _beforeTokenTransfer function on ERC721 very recently(2022-11-08). Please refer the changelog via the following link.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7c5f6bc2c8743d83443fa46395d75f2f3f99054a/CHANGELOG.md#breaking-changes
Adding the additional argument "uint256 batchSize" resolved the compilation issue. Thank you.

Erc4907 is an extention of Erc721 which you may already know. so when calling super._beforeTokenTransfer() you are actually tapping ERC721's function.
If you go to ERC721 you can see that indeed it takes 4 arguments for
function _beforeTokenTransfer(
address from,
address to,
uint256, /* firstTokenId */
uint256 batchSize
) internal virtual {}
You can visit the oz erc721 github page for more.
The argument you are missing is uint256 batchSize.

Related

Receiving a DeclarationError: Identifier not found or not unique while using counters using Remix

I am trying to run the code which is generating an error stating that DeclarationError: Identifier not found or not unique.
The smart contract NFTCollection is a solidity code which enables me to mint a specific number of ERC721 Tokens and to count the tokens of owner. I am receiving a error while displaying the error message "DeclarationError: Identifier not found or not unique" at line 20 which reads "using Counters for Counters.counter;"
Please find the code below.
Any help would be highly appreciated.
The contract and the error message.
pragma solidity ^0.8.4;
// Importing required libraries
/*
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/591c12d22de283a297e2b551175ccc914c938391/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/591c12d22de283a297e2b551175ccc914c938391/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/591c12d22de283a297e2b551175ccc914c938391/contracts/utils/Counters.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/591c12d22de283a297e2b551175ccc914c938391/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
*/
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
// This is a simple NFT contract
//Start of a contract
//INitialising contract as a ERC721, ERC721Enumerable, Ownable contract.
contract NFTCollection is ERC721, ERC721Enumerable, Ownable {
using Counters for Counters.counter;
// Specifing the maximum supply
uint256 maxSupply = 1000000;
// Initiatina a counter to store to be able to use Counters on uint265 without impacting the rest of the uint256 in the contract.
//Counter helps to find every function from a single library to a type to reduce gass fee.
Counters.Counter private _tokenIDCounter;
// Initialising a constructor.
constructor(string memory _name, string memory _symbol)
ERC721(_name, _symbol){}
// ERC721 interface is initialised and the _name, _symbol details aere passed in to hold.
//End of constructor.
//Start of function mint.
//This function is used to mint ERC721 tokens.
//Mint function adds the capability to mint ERC721 tokens and assign it to a address which accepts user address, number of tokens as arguments.
function mint(address _user, uint256 _amount) public {
//Start of for loop
for(uint256 i;i<_amount;i++)
{
//Incrementing the _tokenIdCounter by 1 using increment function.
_tokenIdCounter.increment();
// Creating a variable which stores the current value of _tokenIdCounter.
uint256 tokenId = _tokenIdcounter.current();
//Using safemint to mint ERC721 Tokens.
_safeMint(_user,tokenId);
}
}//End of function mint.
//Start of function tokensOfOwner
// This function is used to keep track of the owners Tokens.
// The function seeks a owner address to check the number of tokens he owns.
function tokensOfOwner(address _owner) public view returns (uint256[] memory)
{ // Creating a ownerTokenCount which stores the balance of the owner.
uint256 ownerTokenCount = balanceOf(_owner);
// Creating a memory variable and storing a array of ownerTokenCount.
uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
for (uint256 i; i < ownerTokenCount; ++i) {
ownedTokenIds[i] = tokenOfOwnerByIndex(_owner, i);
}//End of for loop
return ownedTokenIds; //Return statement.
}
// Function overrides required by solidity
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
Error Message
--> contracts/nft.sol:20:24:
|
20 | using Counters for Counters.counter;~~~

TypeError: Contract "ERC721Enumerable" should be marked as abstract

So I have this error that says (TypeError: Contract "ERC721Enumerable" should be marked as abstract.) and I am unaware on how should I mark the smart contract as abstract. I'm still an absolute beginner so I have no clue how to do certain things yet.
I think I've had this problem before though I have forgotten how I solved it since that was a long time ago and is out of the range of what I can remember.
also here is the program:
pragma solidity ^0.8.0;
import './ERC721.sol';
contract ERC721Enumerable is ERC721 {
uint256[] private _allTokens;
// CHALLENGE! Write out mapping yourself!!
// mapping from tokenId to position in _allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
// mapping of owner to list of all owner token ids
mapping(address => uint256[]) private _ownedTokens;
// mapping from token ID index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// #notice count NFTs tracked by this contract
// #return A count of valid NFTs tracked by this contract, where each on of
// them has an assigned and queryable ower not equal to the zero address
// #notice Enumerate valid NFT's
// #dev Throws if '_index" >= 'totalSupply()'.
// #param _index A couter less than 'totalSupply()'
// #return The token identifier for the '_index'th NFt,
// (sort order not specified)
//function tokenByIndex(uint256 _index) external view returns (uint256);
// #notice Enumerate NFTs assigned to an owner
// #dev Throws if '_index' >= 'balanceOf(_owner)' or if
// '_owner' is the zero address, representing invalid NFTs.
// #param _owner An address where we are interested in NFTs owned by them
// #param _index A counter less than 'balanceOf(_owner)'
// #return The token identifier for the '_index'th NFT assigned to '_owner',
// (sort order not specfied)
//function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
function _mint(address to, uint256 tokenId) internal override(ERC721) {
super._mint(to, tokenId);
// 2 things!
// a. add tokens too the owner
// b. all tokens to out totalSupply - allToken
_addTokensToAllTokenEnumeration(tokenId);
_addTokensToOwnerEnumeration(to, tokenId);
}
// add tokens to the _allTokens array and set the position of the tokens indexes
function _addTokensToAllTokenEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
// two function - one that returns token by the index
// another one thata retruns tokenByOwnerIndex
function tokenByIndex(uint256 index) public view returns(uint256) {
// make sure that the undex is not our of bounds of the
// total supply
require(index < totalSupply(), 'global index is out of bounds!');
return _allTokens[index];
}
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256){
require(index < balanceOf(owner), 'owner is out of bounds!');
return _allTokens[index];
}
// return the total supply of the _allTokens array
function totalSupply() public view returns(uint256) {
return _allTokens.length;
}
function _addTokensToOwnerEnumeration(address to, uint256 tokenId) public {
_ownedTokensIndex[tokenId] = _ownedTokens[to].length;
_ownedTokens[to].push(tokenId);
}
// #notice Transfer owner ship of an NFT -- THE REAL CALLER IS RESPOSIBLE
// TO CONFIRM THAT '_to' IS CAPABLE OF RECEIVING NFTS OR ELSE
// THEY MAY BE PERMANENTLY LOST
// #dev Throws untess 'msg.sender' is the current owner, an authorized
// operator, or the approved address for this NFT. Throws if '_from' is
// not 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 payable;
} ```
Your contract not fully implement all the functions of the ERC721 contract..
Refer
https://ethereum.stackexchange.com/questions/112724/error-contract-should-be-marked-as-abstract
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol

TypeError: Type address is not implicitly convertible to expected type uint256

help, I'm getting errors on my code saying: TypeError: Type address is not implicitly convertible to expected type uint256. And I am in desperate need of help. I don't know what to do and if you need the full code pls say so
I think it has something to do with the address though I am not quite sure since I am still a beginner, so I am absolutely clueless so far. If you do know how to help me solve please say so.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import './ERC721.sol';
contract ERC721Enumerable is ERC721 {
uint256[] private _allTokens;
// CHALLENGE! Write out mapping yourself!!
// mapping from tokenId to position in _allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
// mapping of owner to list of all owner token ids
mapping(uint256 => uint256[]) private _ownedTokens;
// mapping from token ID index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// #notice count NFTs tracked by this contract
// #return A count of valid NFTs tracked by this contract, where each on of
// them has an assigned and queryable ower not equal to the zero address
// #notice Enumerate valid NFT's
// #dev Throws if '_index" >= 'totalSupply()'.
// #param _index A couter less than 'totalSupply()'
// #return The token identifier for the '_index'th NFt,
// (sort order not specified)
//function tokenByIndex(uint256 _index) external view returns (uint256);
// #notice Enumerate NFTs assigned to an owner
// #dev Throws if '_index' >= 'balanceOf(_owner)' or if
// '_owner' is the zero address, representing invalid NFTs.
// #param _owner An address where we are interested in NFTs owned by them
// #param _index A counter less than 'balanceOf(_owner)'
// #return The token identifier for the '_index'th NFT assigned to '_owner',
// (sort order not specfied)
//function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
function _mint(address to, uint256 tokenId) internal override(ERC721) {
super._mint(to, tokenId);
// 2 things!
// a. add tokens too the owner
// b. all tokens to out totalSupply - allToken
_addTokensToAllTokenEnumeration(tokenId);
_addTokensToOwnerEnumeration(to, tokenId);
}
// add tokens to the _allTokens array and set the position of the tokens indexes
function _addTokensToAllTokenEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
// two function - one that returns token by the index
// another one thata retruns tokenByOwnerIndex
function tokenByIndex(uint256 index) public view returns(uint256) {
// make sure that the undex is not our of bounds of the
// total supply
require(index < totalSupply(), 'global index is out of bounds');
return _allTokens[index];
}
// return the total supply of the _allTokens array
function totalSupply() public view returns(uint256) {
return _allTokens.length;
}
function _addTokensToOwnerEnumeration(address to, uint256 tokenId) public {
_ownedTokensIndex[tokenId] = _ownedTokens[to].length;
_ownedTokens[to].push(tokenId);
}
}
In your code, your mapping is wrong. In your function _addTokensToOwnerEnumeration() you have taken address as mapping key. but in your declaration you have taken
//this is wrong as per you have used in your function
mapping(uint256=> uint256[]) private _ownedTokens;
// You have to declare like this.
mapping(address=>uint256[]) private _ownedTokens;
_ownedTokens have uint256 as key type it should be address type:
// mapping of owner to list of all owner token ids
mapping(address => uint256[]) private _ownedTokens;

Solidity - why does fallback() get called even though address.call{value:msg.value}("") does not have data?

The following contract calls another contract using an interface method (code to change):
pragma solidity 0.8.7;
interface MyStorage {
function setStorageValue(uint256) external;
}
contract StorageFactory {
uint256 storageValue;
constructor(uint256 _storageValue) {
storage = _storageValue;
}
function initStorage(MyStorage store) public payable {
store.setStorageValue(storageValue);
address payable storeAddress = payable(address(store));
storeAddress.call{value: msg.value}("");
}
}
Following is the StorageContract (code cannot be changed):
pragma solidity 0.8.7;
contract Storage {
int _storageValue;
function setStorageValue(int storageValue) public {
_storageValue = storageValue;
}
receive() external payable {
require(_storageValue == -1 || address(this).balance <= uint(_storageValue), "Invalid storage value");
}
fallback() external {
_storageValue = -1;
}
}
I use a test to call initStorage of the first contract by passing a Storage object, where the test is meant to fail because the value is set to a large amount. But somehow, the fallback() function seems to get called, setting the value to -1. I can't figure out why. Any help is appreciated.
Due to the solidity doc:
The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable.
Your function getting called because there's no overloading for the function
function setStorageValue(uint256 storageValue) public
So change the storageValue from int to uint256 will help.

How to add method id in metamask

I have deployed smart contract using remix IDE, launched with Injected Web3 on Ropsten test network. I could call BuyTokens function within solidity IDE successfully, but when tried to buy tokens with metamask from other address transaction get reverted. I can see the difference between those operations on ropsten.etherscan explorer - the difference is in Input Data field.
Metamask transaction has value 0x and transaction via remix is:
Function: buyTokens() ***
MethodID: 0xd0febe4c
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Token {
// Track how many tokens are owned by each address.
mapping (address => uint256) public balanceOf;
// Modify this section
string public name = "DemoCoin";
string public symbol = "DC";
uint8 public decimals = 8;
uint256 public totalSupply = 1000000000 * (uint256(10) ** decimals);
address public owner;
//uint scaler = 10e18; // == 1 ETH in wei
//uint public coinPrice = 20; //initial price => 20 cents
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
// Initially assign all tokens to the contract's creator.
owner = msg.sender;
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
// Might be executed automaticlly
// https://blog.chronologic.network/schedule-your-transaction-now-using-mycrypto-and-myetherwallet-17b48166b412
// function changeCoinPrice() public {
// uint newCoinPrice;
// require(msg.sender == address(0));
// coinPrice = newCoinPrice;
// }
function buyTokens() public payable {
// msg.value in wei so 1ETH = 10e18
// lets set 0.20 cents for 1 token
uint paidAmount;
require(balanceOf[msg.sender] >= paidAmount);
require(balanceOf[owner] >= value);
uint tokens;
tokens = value/10e14;
balanceOf[owner] -= tokens;
balanceOf[msg.sender] += tokens;
emit Transfer(owner, msg.sender, tokens);
}
function msgSenderBalancce() public view returns (uint) {
return balanceOf[msg.sender];
}
function withDrawEth() public view {
require(msg.sender == owner);
}
}
Why these methods are called diffrently? And how to add method id in metamask? Or am I missing something and this should be handled in other way?
MetaMask has a very basic UI. It only allows transfers of ETH and standardized tokens, but it doesn't show any buttons to call other contract functions. It also doesn't allow creating any custom buttons in their UI.
You'll need to set the data field of the transaction to 0xd0febe4c (which effectively executes the buyTokens() function).
But - they also don't allow specifying the data field value manually in the UI, so you'll need to preset it using the Ethereum provider API.
Your web app connects to the user's MetaMask acccount. It opens a MetaMask window and the user needs to manually confirm the connect.
The web app sends a request to MetaMask specifying the transaction with data field value.
The user confirms the transaction (which now includes the data field value 0xd0febe4c) in their MetaMask UI.