This contract uses a pseudo-random selection algorithm without repetition to assign NFTs from a fixed pool of token IDs.
-
Maximum NFTs:
10 -
Token IDs are minted sequentially (
0to9) -
NFTs are assigned randomly using:
block.timestampblock.prevrandao- user-provided
salt
-
Once an NFT is assigned, it will never be assigned again
The random assignment flow is:
- Generate pseudo-random number
- Convert it into a valid NFT index
- Select an available NFT ID
- Remove it from the available pool
- Transfer NFT to receiver
Randomness is generated using:
uint256 randomNumber = uint256(
keccak256(
abi.encodePacked(
block.timestamp,
block.prevrandao,
salt
)
)
);| 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.
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.
require(
totalAssignedNfts < MAX_LIMIT,
"Rookie: No more NFTs to assign"
);Prevents assigning more than 10 NFTs.
uint256 remainingNftCount = MAX_LIMIT - totalAssignedNfts;Example:
| Assigned | Remaining |
|---|---|
| 0 | 10 |
| 3 | 7 |
| 9 | 1 |
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
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
if (nftIdList[randomNftIdIndex] == 0) {
randomNftId = randomNftIdIndex;
} else {
randomNftId = nftIdList[randomNftIdIndex];
}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.
After selecting an NFT:
if (nftIdList[remainingNftCount - 1] == 0) {
nftIdList[randomNftIdIndex] = remainingNftCount - 1;
} else {
nftIdList[randomNftIdIndex] =
nftIdList[remainingNftCount - 1];
delete nftIdList[remainingNftCount - 1];
}The selected slot is replaced with the last available NFT ID.
This effectively shrinks the available pool by 1.
Assume available NFTs are:
[0,1,2,3,4,5,6,7,8,9]
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.
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.
Each assignment:
- Picks one NFT
- Replaces it with last available NFT
- Reduces available pool size
So already assigned NFTs are never reachable again.
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);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
Current code:
require(tokenId <= MAX_LIMIT, "Rookie: Exceeds Limit");This allows token ID 10 when MAX_LIMIT = 10.
require(tokenId < MAX_LIMIT, "Rookie: Exceeds Limit");The contract uses:
if (nftIdList[index] == 0)where 0 means:
- empty slot
- NFT ID
0
This creates ambiguity.
- start token IDs from
1 - or use mapping-based replacement logic
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
The algorithm is gas efficient because:
- no loops during assignment
- O(1) random selection
- no dynamic array removals
Efficient even for larger NFT collections.
The contract implements:
- pseudo-random NFT assignment
- no duplicate distribution
- efficient random selection
- swap-and-replace pool management
using:
keccak256 + Fisher-Yates style selectionto randomly distribute NFTs from a fixed collection.