Keep track of all minted tokens on ERC721 Smart Contract - ethereum

I have a Smart Contract that I'd like to build a web based marketplace app for. How can I list all available "Items" in the contract. Seeing as there is no upper limit to the number of minted Items, how can I implement pagination and search?
What is a best practice and scalable solution:
to simply return the Items[] array
sync the array with my database on Transfer and other events
other suggestions?
pragma solidity ^0.4.24;
contract Item is ERC721{
struct Item{
string name; // Name of the Item
uint level; // Item Level
uint rarityLevel; // 1 = normal, 2 = rare, 3 = epic, 4 = legendary
}
Item[] public items; // First Item has Index 0
address public owner;
function Item() public {
owner = msg.sender; // The Sender is the Owner; Ethereum Address of the Owner
}
function createItem(string _name, address _to) public{
require(owner == msg.sender); // Only the Owner can create Items
uint id = items.length; // Item ID = Length of the Array Items
items.push(Item(_name,5,1)) // Item ("Sword",5,1)
_mint(_to,id); // Assigns the Token to the Ethereum Address that is specified
}
}

You can create an itemsCount public property that holds the current amount of existing items, and increment it after each items.push().
Without pagination and search:
Off-chain systems that want to read your data, can simply loop from items[0] to items[itemsCount] and since it's just a read operation, it doesn't require a transaction (i.e. it's free).
With pagination and search:
You can to create a view function that:
Takes page and query as arguments
Loops through existing items and if the item fits the criteria (name contains query), add it to the results array
Returns the results array
Note: Step 2 is actually going to be a bit more complicated in Solidity, because you can't push into an in-memory dynamic array. So you need to:
Loop through the items and find an amount that fit the criteria (up to the page limit)
Create a fix-length in-memory array results
Now you can loop through the items again and fill the fix-length results array with values

mapping are more efficient than arrays in solidity. You could have a uint in the smart contract that keeps the count of every Item and use a getter function to get each item from whatever number you want to paginate subtracted from the item count.
contract Item is ERC721{
struct Item{
string name; // Name of the Item
uint level; // Item Level
uint rarityLevel; // 1 = normal, 2 = rare, 3 = epic, 4 = legendary
uint id;
}
mapping(uint => Item) items; // First Item has Index 0
uint count;
address public owner;
function Item() public {
owner = msg.sender; // The Sender is the Owner; Ethereum Address of the Owner
}
function createItem(string memory _name, address _to, uint level, uint rarityLevel
uint id) public{
require(owner == msg.sender); // Only the Owner can create Items
uint num = count++;
items[num].name = _name;
items[num].level = level;
items[num].rarityLevel = rarityLevel;
items[num].id = num;
count++;
}
function getItem(uint item) public view returns(string memory _name, uint level,
uint rarityLevel, uint id){
uint level = items[item].level;
uint rarityLevel = items[item].rarityLevel;
uint id = items[item].id;
string memory _name = items[item].name
return(level, rarityLevel, id, _name)
}

Related

Ordinal Number Function in Remix Solidity

I want to create a new function based on these codes below, that it can just fill in the ordinal number (first student is started by 1) to track out the struct student info in this case.
That's mean after I give the data of this first student to the struct by addInfo function:["Simon", 20, "CA USA", 10] Then my expected new function is that I just fill in this unit: 1, then it will appear this student detail
\`
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReferenceTypes {
address public owner;
struct student {
string Name;
uint Age;
string BirthPlace;
uint8 Marks;
}
constructor() {
owner = msg.sender;
}
mapping (address => student) public Info;
address[] public student_Info;
function addInfo(student memory _student, address _address) public {
require(owner == msg.sender, "Only admin can add Info!!!");
Info[_address] = _student;
student_Info.push(_address);
}
function count() public view returns(uint) {
return student_Info.length;
}
}
\`
Thanks so much guys
I tried a new mapping but failed
You can make another mapping that leads to your mapping Info.
uint public studentsCounter;
mapping (uint => mapping (address => student)) public Info;
function addInfo(student memory _student, address _address) public returns(uint){
require(owner == msg.sender, "Only admin can add Info!!!");
Info[studentsCounter][_address] = _student;
studentsCounter++;
student_Info.push(_address);
return studentsCounter-1;
}
Thus you can reach your struct by passing studentId(studentCounter) which addInfo returns and students address.
Another approach is to remake you array of addresses.
student[] public student_Info;
And push struct in this array.
function addInfo(student memory _student, address _address) public {
require(owner == msg.sender, "Only admin can add Info!!!");
Info[_address] = _student;
student_Info.push(_student);
}
So now you can display studentsInfo buy passing index in student_Info array.

ParserError: Expected '{' but got 'constant' in Remix IDE

Below is my code.`
pragma solidity ^0.8.0;
contract Auction {
// Data
//Structure to hold details of the item
struct Item {
uint itemId; // id of the item
uint[] itemTokens; //tokens bid in favor of the item
}
//Structure to hold the details of a persons
struct Person {
uint remainingTokens; // tokens remaining with bidder
uint personId; // it serves as tokenId as well
address addr;//address of the bidder
}
mapping(address => Person) tokenDetails; //address to person
Person [4] bidders;//Array containing 4 person objects
Item [3] public items;//Array containing 3 item objects
address[3] public winners;//Array for address of winners
address public beneficiary;//owner of the smart contract
uint bidderCount=0;//counter
//functions
function Auction() public payable{ //constructor
//Part 1 Task 1. Initialize beneficiary with address of smart contract’s owner
//Hint. In the constructor,"msg.sender" is the address of the owner.
// ** Start code here. 1 line approximately. **/
beneficiary = msg.sender;
//** End code here. **/
uint[] memory emptyArray;
items[0] = Item({itemId:0,itemTokens:emptyArray});
//Part 1 Task 2. Initialize two items with at index 1 and 2.
// ** Start code here. 2 lines approximately. **/
uint[] memory emptyArray1;
items[1] = Item({itemId:1,itemTokens:emptyArray1});
uint[] memory emptyArray2;
items[2] = Item({itemId:2,itemTokens:emptyArray2});
//** End code here**/
}
function register() public payable{
bidders[bidderCount].personId = bidderCount;
//Part 1 Task 3. Initialize the address of the bidder
/*Hint. Here the bidders[bidderCount].addr should be initialized with address of the registrant.*/
// ** Start code here. 1 line approximately. **/
bidders[bidderCount].addr = msg.sender;
//** End code here. **
bidders[bidderCount].remainingTokens = 5; // only 5 tokens
tokenDetails[msg.sender]=bidders[bidderCount];
bidderCount++;
}
function bid(uint _itemId, uint _count) public payable{
/*
Bids tokens to a particular item.
Arguments:
_itemId -- uint, id of the item
_count -- uint, count of tokens to bid for the item
*/
/*
Part 1 Task 4. Implement the three conditions below.
4.1 If the number of tokens remaining with the bidder is < count of tokens bidded, revert.
4.2 If there are no tokens remaining with the bidder, revert.
4.3 If the id of the item for which bid is placed, is greater than 2, revert.
Hint: "tokenDetails[msg.sender].remainingTokens" gives the details of the number of tokens remaining with the bidder.
*/
// ** Start code here. 2 lines approximately. **/
if ( (tokenDetails[msg.sender].remainingTokens < _count) ||
(tokenDetails[msg.sender].remainingTokens <= 0)
) { revert(); }
if (_itemId > items.length - 1) { revert(); }
//** End code here. **
/*Part 1 Task 5. Decrement the remainingTokens by the number of tokens bid and store the value in balance variable.
Hint. "tokenDetails[msg.sender].remainingTokens" should be decremented by "_count". */
// ** Start code here. 1 line approximately. **
uint balance = tokenDetails[msg.sender].remainingTokens - _count;
//** End code here. **
tokenDetails[msg.sender].remainingTokens=balance;
bidders[tokenDetails[msg.sender].personId].remainingTokens=balance;//updating the same balance in bidders map.
Item storage bidItem = items[_itemId];
for(uint i=0; i<_count;i++) {
bidItem.itemTokens.push(tokenDetails[msg.sender].personId);
}
}
// Part 2 Task 1. Create a modifier named "onlyOwner" to ensure that only owner is allowed to reveal winners
//Hint : Use require to validate if "msg.sender" is equal to the "beneficiary".
modifier onlyOwner {
// ** Start code here. 2 lines approximately. **
require (msg.sender == beneficiary);
_;
//** End code here. **
}
function revealWinners() public onlyOwner{
/*
Iterate over all the items present in the auction.
If at least on person has placed a bid, randomly select the winner */
for (uint id = 0; id < 3; id++) {
Item storage currentItem=items[id];
if(currentItem.itemTokens.length != 0){
// generate random# from block number
uint randomIndex = (block.number / currentItem.itemTokens.length)% currentItem.itemTokens.length;
// Obtain the winning tokenId
uint winnerId = currentItem.itemTokens[randomIndex];
/* Part 1 Task 6. Assign the winners.
Hint." bidders[winnerId] " will give you the person object with the winnerId.
you need to assign the address of the person obtained above to winners[id] */
// ** Start coding here *** 1 line approximately.
winners[id] = bidders[winnerId].addr;
//** end code here*
}
}
}
//Miscellaneous methods: Below methods are used to assist Grading. Please DONOT CHANGE THEM.
function getPersonDetails(uint id) public constant returns(uint,uint,address){
return (bidders[id].remainingTokens,bidders[id].personId,bidders[id].addr);
}
}`
I am trying to compile it but I am unable to do it.
please help in this regard
thanks

Why am i getting an error on the array length in solidity?

I am developing an NFT smart contract with solidity and am encountering a strange error. I use some functions to add data to mappings inside the contract, both of them loop through arrays that are passed into the contract. They look like this.
function addNameDescImagePricesTowers(
uint[] memory ids, string[] memory propertyNames, uint[] memory prices,
string[] memory descs, string[] memory images, string[] memory towers, string[] memory districts, string[] memory neighs
) public onlyOwner() {
console.log("Started to add base data");
for (uint i = 0; i < ids.length; i ++){
defaultProperties[ids[i]-1] =
PropertyAttributes({
id: ids[i],
propertyIndex: ids[i]-1,
name: propertyNames[i],
description: descs[i],
image:images[i],
features: Features({
tower: towers[i],
district: districts[i],
neighborhood: neighs[i],
primary_type: '',//primeTypes[i],
sub_type_1: '',//subType1[i],
sub_type_2: '',//subType2[i],
structure: '',//structures[i],
feature_1: '',//feature1[i],
feature_2: '',//feature2[i],
feature_3: '',//feature3[i],
feature_4: '',//feature4[i],
feature_5: '',//feature5[i],
feature_6: ''//raritys[i]
}),
extensionCount: 0
});
//created minted list with all false
MintedNfts[ids[i]] = false;
//record the mint price
MintPrice[ids[i]] = prices[i] * (1 ether);
}
console.log("added base data");
}
function addNftData(uint[] memory ids, string[] memory primes,string[] memory subs1, string[] memory subs2, string[] memory structures, string[] memory fets1,
string[] memory fets2, string[] memory fets3, string[] memory fets4, string[] memory fets5, string[] memory fets6
) public onlyOwner() {
for (uint i = 0; i < ids.length; i ++){
uint id = ids[i];
PropertyAttributes memory x = defaultProperties[id-1];
// x.neighborhood = neighs[i];
x.features.primary_type = primes[i];
x.features.sub_type_1 = subs1[i];
x.features.sub_type_2 = subs2[i];
x.features.structure = structures[i];
x.features.feature_1 = fets1[i];
x.features.feature_2 = fets2[i];
x.features.feature_3 = fets3[i];
x.features.feature_4 = fets4[i];
x.features.feature_5 = fets5[i];
x.features.feature_6 = fets6[i];
defaultProperties[i] = x;
}
console.log("added the core data");
}
However when i run the contract I get an error on the second function:
TypeError: Cannot read property 'length' of undefined
As I am passing the exactly the same ids array into the function each time I don't understand why the first one runs fine but the second gives the error?
Here is the js code that runs the functions:
let txn;
//add basic property detials
txn = await propertyContract.addNameDescImagePricesTowers(ids,names,prices,descs,images, towers,districts)
await txn.wait();
//add remainign detials
txn = await propertyContract.addNftData( ids, neighs, primes, sub1s, sub2s, structures, fet1s, fet2s, fet3s, fet4s, fet5s, raritys);
await txn.wait();
The whole contract looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;
import "hardhat/console.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
//import '#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';
//access control
import "#openzeppelin/contracts/access/Ownable.sol";
// Helper functions OpenZeppelin provides.
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
import "./libraries/Base64.sol";
contract MetropolisWorldGenesis is ERC721, Ownable {
// The tokenId is the NFTs unique identifier, it's just a number that goes
// 0, 1, 2, 3, etc.
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
string private _contractURI;
struct Extension {
string catergory;
string name;
string contractId;
string tokenId;
}
struct PropertyAttributes {
uint id;
uint propertyIndex;
string name;
string description;
string image;
Features features;
uint extensionCount;
}
struct Features {
string tower;
string district;
string neighborhood;
string primary_type;
string sub_type_1;
string sub_type_2;
string structure;
string feature_1;
string feature_2;
string feature_3;
string feature_4;
string feature_5;
string feature_6;
}
//store a list of all the NFT's available to mint.
//this is built on when the constructor is called.
mapping(uint => PropertyAttributes) defaultProperties;
//PropertyAttributes[] defaultProperties;
//store which has been minted.
mapping(uint => bool) public MintedNfts;
//store mint prices
mapping(uint => uint) public MintPrice;
//map the nft tokenid to the atributes
mapping(uint256 => PropertyAttributes) public nftHolderAttributes;
//map extensions to the token id.
mapping(uint256 => mapping(uint => Extension)) extensions;
// A mapping from an address => the NFTs tokenId. Gives me an ez way
// to store the owner of the NFT and reference it later.
mapping(uint256 => address) public nftHolders;
constructor() ERC721("Metropolis World Genesis", "METGEN") {
console.log("OK I am making the contract, just this once mind");
// I increment _tokenIds here so that my first NFT has an ID of 1.
_tokenIds.increment();
}
function contractURI() public view returns (string memory) {
return _contractURI;
}
function addNameDescImagePricesTowers(
uint[] memory ids, string[] memory propertyNames, uint[] memory prices,
string[] memory descs, string[] memory images, string[] memory towers, string[] memory districts, string[] memory neighs
) public onlyOwner() {
console.log("Started to add base data");
for (uint i = 0; i < ids.length; i ++){
defaultProperties[ids[i]-1] =
PropertyAttributes({
id: ids[i],
propertyIndex: ids[i]-1,
name: propertyNames[i],
description: descs[i],
image:images[i],
features: Features({
tower: towers[i],
district: districts[i],
neighborhood: neighs[i],
primary_type: '',//primeTypes[i],
sub_type_1: '',//subType1[i],
sub_type_2: '',//subType2[i],
structure: '',//structures[i],
feature_1: '',//feature1[i],
feature_2: '',//feature2[i],
feature_3: '',//feature3[i],
feature_4: '',//feature4[i],
feature_5: '',//feature5[i],
feature_6: ''//raritys[i]
}),
extensionCount: 0
});
//created minted list with all false
MintedNfts[ids[i]] = false;
//record the mint price
MintPrice[ids[i]] = prices[i] * (1 ether);
}
console.log("added base data");
}
function addNftData(uint[] memory ids, string[] memory primes,string[] memory subs1, string[] memory subs2, string[] memory structures, string[] memory fets1,
string[] memory fets2, string[] memory fets3, string[] memory fets4, string[] memory fets5, string[] memory fets6
) public onlyOwner() {
for (uint i = 0; i < ids.length; i ++){
uint id = ids[i];
PropertyAttributes memory x = defaultProperties[id-1];
// x.neighborhood = neighs[i];
x.features.primary_type = primes[i];
x.features.sub_type_1 = subs1[i];
x.features.sub_type_2 = subs2[i];
x.features.structure = structures[i];
x.features.feature_1 = fets1[i];
x.features.feature_2 = fets2[i];
x.features.feature_3 = fets3[i];
x.features.feature_4 = fets4[i];
x.features.feature_5 = fets5[i];
x.features.feature_6 = fets6[i];
defaultProperties[i] = x;
}
console.log("added the core data");
}
//Public payable mint
//check enough eth sent
function publicMint(uint id) public payable {
//get current tokenId (starts at 1)
uint256 newItemId = _tokenIds.current();
uint _propertyIndex = id - 1;
//check if minted
require(MintedNfts[_propertyIndex]==false, "Already Minted");
//check if paid enough
require(MintPrice[_propertyIndex] <= msg.value, "Not enough eth paid");
_safeMint(msg.sender, newItemId);
nftHolderAttributes[newItemId] = PropertyAttributes({
id: defaultProperties[_propertyIndex].id,
propertyIndex: _propertyIndex,
name: defaultProperties[_propertyIndex].name,
description: defaultProperties[_propertyIndex].description,
image: defaultProperties[_propertyIndex].image,
features: Features({
tower: defaultProperties[_propertyIndex].features.tower,
district: defaultProperties[_propertyIndex].features.district,
neighborhood: defaultProperties[_propertyIndex].features.neighborhood,
primary_type: defaultProperties[_propertyIndex].features.primary_type,
sub_type_1: defaultProperties[_propertyIndex].features.sub_type_1,
sub_type_2: defaultProperties[_propertyIndex].features.sub_type_2,
structure: defaultProperties[_propertyIndex].features.structure,
feature_1: defaultProperties[_propertyIndex].features.feature_1,
feature_2: defaultProperties[_propertyIndex].features.feature_2,
feature_3: defaultProperties[_propertyIndex].features.feature_3,
feature_4: defaultProperties[_propertyIndex].features.feature_4,
feature_5: defaultProperties[_propertyIndex].features.feature_5,
feature_6: defaultProperties[_propertyIndex].features.feature_6
}),
extensionCount: 0
});
console.log("Minted NFT w/ tokenId %s and characterIndex %s", newItemId, _propertyIndex);
// Keep an easy way to see who owns what NFT. Maps token id to address
nftHolders[newItemId] = msg.sender;
// update the minted list
MintedNfts[_propertyIndex] = true;
// Increment the tokenId for the next person that uses it.
_tokenIds.increment();
}
//free owner only mint
function mintPropertyNft(uint id, address toWallet) public onlyOwner(){ //payable
//get current tokenId (starts at 1)
uint256 newItemId = _tokenIds.current();
uint _propertyIndex = id - 1;
//check if minted
require(MintedNfts[_propertyIndex]==false, "Already Minted");
_safeMint(toWallet, newItemId);
nftHolderAttributes[newItemId] = PropertyAttributes({
id: defaultProperties[_propertyIndex].id,
propertyIndex: _propertyIndex,
name: defaultProperties[_propertyIndex].name,
description: defaultProperties[_propertyIndex].description,
image: defaultProperties[_propertyIndex].image,
features: Features({
tower: defaultProperties[_propertyIndex].features.tower,
district: defaultProperties[_propertyIndex].features.district,
neighborhood: defaultProperties[_propertyIndex].features.neighborhood,
primary_type: defaultProperties[_propertyIndex].features.primary_type,
sub_type_1: defaultProperties[_propertyIndex].features.sub_type_1,
sub_type_2: defaultProperties[_propertyIndex].features.sub_type_2,
structure: defaultProperties[_propertyIndex].features.structure,
feature_1: defaultProperties[_propertyIndex].features.feature_1,
feature_2: defaultProperties[_propertyIndex].features.feature_2,
feature_3: defaultProperties[_propertyIndex].features.feature_3,
feature_4: defaultProperties[_propertyIndex].features.feature_4,
feature_5: defaultProperties[_propertyIndex].features.feature_5,
feature_6: defaultProperties[_propertyIndex].features.feature_6
}),
extensionCount: 0
});
console.log("Minted NFT w/ tokenId %s and characterIndex %s", newItemId, _propertyIndex);
// Keep an easy way to see who owns what NFT. Maps token id to address
nftHolders[newItemId] = msg.sender;
// update the minted list
MintedNfts[_propertyIndex] = true;
// Increment the tokenId for the next person that uses it.
_tokenIds.increment();
}
}
Any suggestions gratefully received.

How to push element in multiple dimension array at certain coordinate in solidity?

I have an array like this:
Animal[3][2][] animalArray.
After I have a new element like "dog", how to push it in the certain place at like
Animal[2][1][]?
I tried
animalArray[2][1].push(dog);
It always gives me the error.
Here below is the code with the problem:
pragma solidity ^0.4.0;
contract Zootest {
struct Zoo {
uint state;
Animal[][2][3] animalarray;
uint price;
}
struct Animal {
uint quantity;
address Address;
}
mapping (uint => Zoo) zoo;
function openZoo (uint index) {
Zoo memory newZoo = Zoo({
state: 1,
price: 0,
animalarray: new Animal[][2][3](0)
});
zoo[index] = newZoo;
}
function enterZoo (uint index, uint x, uint y, uint quantity) public {
Animal memory newAnimal = Animal({
Address:msg.sender,
quantity:quantity
});
zoo[index].price = zoo[index].price+msg.value;
zoo[index].animalarray[x][y].push(newAnimal);
}
}
Perhaps you meant to declare the array this way:
Animal[][2][3] animalArray;
From https://solidity.readthedocs.io/en/v0.4.24/types.html#arrays (my emphasis):
An array of fixed size k and element type T is written as T[k], an array of dynamic size as T[]. As an example, an array of 5 dynamic arrays of uint is uint[][5] (note that the notation is reversed when compared to some other languages). To access the second uint in the third dynamic array, you use x[2][1] (indices are zero-based and access works in the opposite way of the declaration, i.e. x[2] shaves off one level in the type from the right).

What is the best approach in Solidity for pagination (straightforward and based on some filter(s)) capabilities?

Let's suppose that we have following structures in Solidity contract :
struct EntityA {
string lessThen32ByteString1;
string moreThen32ByteString1;
string lessThen32ByteString2;
string moreThen32ByteString3;
bool flag;
uint var1;
uint var2;
uint var3;
uint var4;
ProposalStatus proposalStatus;}
// 100K entities EntityA[] public items;
We have now following implementation (with helper functions for string to byte32 conversion, splitting string to a few byte32 parts and so on) :
function getChunkOfPart1EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString1Arr, bytes32[100] moreThen32ByteString1PrefixArr, bytes32[100] moreThen32ByteString1SuffixArr) {
}
function getChunkOfPart2EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString2Arr, bytes32[100] moreThen32ByteString2PrefixArr, bytes32[100] moreThen32ByteString2SuffixArr) {
}
function getChunkOfPart3EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bool[100] flagArr, uint[100] var1Arr, uint[100] var2Arr, uint[100] var3Arr, uint[100] var4Arr, ProposalStatus[100] proposalStatusArr,) {
}
Current implementation does not look good from design perspective, furthermore each additional filter support requires contract changes due to absence any query language support.
Use case example : retrieve 10 entities based on specified proposalStatus and offset 50 (6th page, page size is 10 entities (maybe 20, 50, 100))
I know I'm late but here you go. This will return an array of uints. You can then use this array on the client to resolve the id's in the getter for the array that stores your data.
struct Zombie {
//data
};
Zombie[] public zombies;
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (uint[]) {
Zombie[] memory result = new Zombie[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = i;
} //CONVERT TO SAFEMATH
return result;
}
So once you get this returned you can .map() the result in your client app and call zombies(uint _index).
If you need more details on this approach please check out cryptozombies.io
Not sure if this is the best approach though.
Hopefully in the future you can just do:
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (Zombie[]) {
uint[] memory result = new uint[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = zombies[i];
} //CONVERT TO SAFEMATH
return result;
}