1

I'm currently building an NFT fantasy league game on ethereum where I want to add the functionality of on demand minting of random tokens. For that I need to keep track of the tokens that are available for minting so that I can choose random ones from them. The only strategy I could find was to store the token Ids for a player in an array, and pop the id after minting. This eliminates any chance of collisions since the token now doesn't exist in the array anymore and if I land on the same index, a new Id is there.

But the problem is I can't find any way of populating an array so big (in my case 40000 integers from 1-40000). It runs out of gas if I use a for loop and populate it in the smart contract or even if I pass the entire array from javascript as an argument in a function.

Is there any way to populate arrays of this size other than doing it in batches using multiple transactions (it allows me to send an array 1000 integers to the contract but that leads to way to many transactions when I have hundreds of players each having 40 thousand tokens). Also if there's a different way to accomplish this task other than using arrays I'd be open to that too.

pragma solidity ^0.8.9;

contract test {

mapping(uint16=>uint16[]) public availableTokens;
//Player=>tokens mapping

function pushNumbers(uint16 player) public {
        uint16 i = 0; //tokens
        
        for (i=0; i<40000; i++){
          availableTokens[player].push(i);
        }
        
    }
//Runs out of gas

}

2 Answers 2

1

Using a counter instead of an array to track players and their minting would be a much better approach. In the code example I provided here, they are allowed to mint up to 40 000 tokens with random ID's. Anything more than that fails.

Here is rough "HelloWorld" example:

NOTE: Not sure I get your functionality 100% but this might give you an idea.

pragma solidity ^0.8.9;

contract test {

    // Player => count of tokens mapping
    mapping(uint16 => uint16) public availableTokens;

    // Player => token => minted status mapping      
    mapping(uint16 => mapping(uint16 => bool)) public mintedTokens;

    // Used for tracking a number of minted tokens
    uint16 constant TOTAL_TOKENS = 40000;

    function initializeTokens(uint16 player) public {
        // Initialize the count of tokens for the player
        availableTokens[player] = TOTAL_TOKENS;
    }

    function mintToken(uint16 player) public {
        // Ensure the player has tokens left to mint
        require(availableTokens[player] > 0, "No tokens left for this player");

        // This is ok for local tests. But for generating randomness you need to use Chainlink
        uint16 tokenId = uint16((block.timestamp + block.difficulty) % TOTAL_TOKENS);

        // Check if token was already minted
        require(!mintedTokens[player][tokenId], "Token already minted, try again");

        // Mint the token with the generated ID here (pseudo code)
        // MintToken(player, tokenId);

        // Mark token as minted
        mintedTokens[player][tokenId] = true;

        // Decrease the count of available tokens for the player
        availableTokens[player]--;
    }
}
2
  • Appreciate you taking the time for this. But the problem is I have to pick a random token from the remaining ones instead of returning an error if it doesn't land. Even if I remove the condition and keep generating a random number until I find one, in the worst case scenario it will be very difficult to land on the available tokens. Like if 39999 tokens are minted and one is left it will be near impossible to get it. That's the problem arrays solve. You only have the remaining ones left in them and you can use the array length as the the max range for the random number to land on an index. Jul 13, 2023 at 10:09
  • Alright, I think I understand your business logic much better now. But I have a few questions. Why do you need a list/array of tokenID where you pick a random one from there?? tokenID is there just so each token can be unique. We can do that without the array that already has bunch tokenID's listed?
    – Sky
    Jul 13, 2023 at 13:08
0

So considering your explanation, there are a few things to think about.

First, do you really need an array/List with all 40_000 tokenIDs ?? You can By that logic, each player could technically mint all 40_000 tokens for himself. Meaning there would be a lot of duplicate tokeIDs. Is every tokenID presenting different type of charahter in fantasy league game ?? Like a football card?? So people can technically have duplicates??

If not, I would just randomly generate tokenID, I would not be getting it from pre-set list.

//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.3;

contract Test {
    // Player => count of tokens mapping
    mapping(uint16 => uint16) public availableTokens;

    // Player => token => minted status mapping      
    mapping(uint16 => mapping(uint256 => bool)) public mintedTokens;

    // Used for tracking a number of minted tokens
    uint16 constant MAX_TOKENS = 40000;


    function mintToken(uint16 player) public {
        // Ensure the player still has tokens left to mint (No more than 40_000)
        require(availableTokens[player] < MAX_TOKENS, "No tokens left for this player");

        uint256 tokenId = generateTokenId();

        // Check if token was already minted 
        // (Odds of colision here are null, but it protects you if there as an exploit in your randomness)
        while(mintedTokens[player][tokenId]){
            tokenId = generateTokenId();
        }

        // Mint the token with the generated ID here (pseudo code)
        // MintToken(player, tokenId);

        // Mark token as minted
        mintedTokens[player][tokenId] = true;

        // Decrease the count of available tokens for the player
        availableTokens[player]++;
    }
    
    function generateTokenId() private view returns (uint256){
        // This is just for local testing, in live implementation you should use ChainLink)
        return uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty, msg.sender)));
    }
}

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.