Skip to content

Repository files navigation

Random NFT Assignment Logic

This contract uses a pseudo-random selection algorithm without repetition to assign NFTs from a fixed pool of token IDs.


Overview

  • Maximum NFTs: 10

  • Token IDs are minted sequentially (0 to 9)

  • NFTs are assigned randomly using:

    • block.timestamp
    • block.prevrandao
    • user-provided salt
  • Once an NFT is assigned, it will never be assigned again

The random assignment flow is:

  1. Generate pseudo-random number
  2. Convert it into a valid NFT index
  3. Select an available NFT ID
  4. Remove it from the available pool
  5. Transfer NFT to receiver

Random Number Generation

Randomness is generated using:

uint256 randomNumber = uint256(
    keccak256(
        abi.encodePacked(
            block.timestamp,
            block.prevrandao,
            salt
        )
    )
);

Components Used

Component Purpose
block.timestamp Current block timestamp
block.prevrandao Randomness value provided by Ethereum validator
salt Custom value passed by contract owner

The output of keccak256 is converted into a uint256.


Random NFT Selection Logic

The contract maintains an internal array:

uint256[MAX_LIMIT] nftIdList;

This array works as a lightweight mapping system to track which NFT IDs are still available.


Assignment Flow

Step 1 — Ensure NFTs Are Available

require(
    totalAssignedNfts < MAX_LIMIT,
    "Rookie: No more NFTs to assign"
);

Prevents assigning more than 10 NFTs.


Step 2 — Calculate Remaining NFTs

uint256 remainingNftCount = MAX_LIMIT - totalAssignedNfts;

Example:

Assigned Remaining
0 10
3 7
9 1

Step 3 — Pick Random Index

uint256 randomNftIdIndex =
    randomNumber % remainingNftCount;

This converts the large random number into a valid index range.

Example:

remainingNftCount = 7

Possible indexes:
0,1,2,3,4,5,6

How Unique NFT Assignment Works

This contract uses a technique similar to:

  • Fisher-Yates shuffle
  • Swap-and-pop random selection

The goal is:

  • pick random NFT
  • avoid duplicates
  • avoid looping through arrays

Core Logic

Selecting NFT ID

if (nftIdList[randomNftIdIndex] == 0) {
    randomNftId = randomNftIdIndex;
} else {
    randomNftId = nftIdList[randomNftIdIndex];
}

Explanation

If the slot is empty (0):

  • the index itself is treated as NFT ID

Otherwise:

  • use stored replacement value

This allows the contract to simulate removal of assigned IDs without shifting array elements.


Removing Assigned NFT From Pool

After selecting an NFT:

if (nftIdList[remainingNftCount - 1] == 0) {
    nftIdList[randomNftIdIndex] = remainingNftCount - 1;
} else {
    nftIdList[randomNftIdIndex] =
        nftIdList[remainingNftCount - 1];

    delete nftIdList[remainingNftCount - 1];
}

What Happens Here

The selected slot is replaced with the last available NFT ID.

This effectively shrinks the available pool by 1.


Example Walkthrough

Assume available NFTs are:

[0,1,2,3,4,5,6,7,8,9]

First Assignment

Random index:

3

Selected NFT:

3

Now replace index 3 with last available NFT (9):

[0,1,2,9,4,5,6,7,8]

NFT 3 is now removed from pool.


Second Assignment

Random index:

3

Now index 3 contains:

9

Selected NFT:

9

Replace with last available NFT (8):

[0,1,2,8,4,5,6,7]

NFT 9 removed.


Why No Duplicate NFTs Are Assigned

Each assignment:

  1. Picks one NFT
  2. Replaces it with last available NFT
  3. Reduces available pool size

So already assigned NFTs are never reachable again.


Transfer Process

After random NFT ID is selected:

transferFrom(msg.sender, receiver, randomNftId);

The NFT is transferred from contract owner to receiver.

Event emitted:

emit NFTAssigned(receiver, randomNftId);

Important Notes

Pseudo-Randomness Warning

This randomness is not cryptographically secure.

Because it depends on block variables:

  • validators/miners may influence values slightly
  • predictable under certain conditions

Suitable for:

  • small NFT collections
  • low-value distributions
  • educational/demo projects

Not suitable for:

  • high-value lottery systems
  • gambling
  • provably fair randomness

Known Issues / Flaws

1. Off-by-One Bug

Current code:

require(tokenId <= MAX_LIMIT, "Rookie: Exceeds Limit");

This allows token ID 10 when MAX_LIMIT = 10.

Correct Version

require(tokenId < MAX_LIMIT, "Rookie: Exceeds Limit");

2. Token ID 0 Sentinel Issue

The contract uses:

if (nftIdList[index] == 0)

where 0 means:

  • empty slot
  • NFT ID 0

This creates ambiguity.

Better Approaches

  • start token IDs from 1
  • or use mapping-based replacement logic

3. Randomness Can Be Manipulated

The randomness source is not fully secure because block variables can be influenced slightly by validators.

For production-grade randomness, use:

  • Chainlink VRF
  • commit-reveal scheme

Gas Efficiency

The algorithm is gas efficient because:

  • no loops during assignment
  • O(1) random selection
  • no dynamic array removals

Efficient even for larger NFT collections.


Summary

The contract implements:

  • pseudo-random NFT assignment
  • no duplicate distribution
  • efficient random selection
  • swap-and-replace pool management

using:

keccak256 + Fisher-Yates style selection

to randomly distribute NFTs from a fixed collection.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages