NFT Rarity System Development
Rarity score — what turns uniform collection tokens into hierarchy with different market values. Without thought-out rarity system, 10,000 token collection trades as homogeneous mass where price determined only by floor. With right system — top-1% costs 10–50x more than floor. This directly impacts liquidity and trader interest. Technically task consists of two parts: off-chain generation and ranking, and on-chain verification via Merkle tree or metadata storage.
Rarity Score Calculation Algorithms
Statistical Rarity (Rarity Tools Method)
Classic approach: for each attribute calculate frequency in collection. Score = sum of 1 / trait_frequency for all token attributes.
# Pseudocode
for nft in collection:
score = 0
for trait_type, trait_value in nft['attributes']:
frequency = count(trait_value) / total_supply
score += 1 / frequency
nft['rarity_score'] = score
Problem with statistical rarity: trait count bias. Token with 10 common attributes can get higher score than token with 5 attributes, one of which is unique (1/10000). This is counterintuitive for users and creates disputes about "fairness".
Information Content Rarity (Rarity Sniper Method)
Uses information-theoretic approach: each attribute contributes proportional to information content -log2(probability).
IC(trait) = -log2(count(trait) / total_supply)
Attribute with 0.1% frequency (1 of 1000) gives IC = log2(1000) ≈ 10 bits. Attribute with 50% frequency gives IC = 1 bit. Final score = sum of IC for all attributes. More fair metric that doesn't penalize attribute count.
Normalized Score for Single-Attribute Rarities
If collection has trait type "background" with 20 variants and trait "special" with 2 variants (one appears in 1 token), normalization allows comparing contribution of different trait types on unified scale:
normalized_score(trait) = rarity_score(trait) / max_rarity_score(trait_type)
Algorithm choice depends on collection structure and marketing strategy. For "Legendary" tier collections — statistical approach with trait type weighting works better.
Technical Implementation Stack
Generation and Calculation (Off-Chain)
Python script with three stages:
- Trait analysis — parse all JSON metadata, build frequency table for each trait_type/trait_value
- Score calculation — chosen algorithm, normalization, ranking
-
Output — updated JSON files with added fields
rarity_score,rarity_rank
Key moment: metadata updated before IPFS upload. After pin CID fixed — can't change rarity score without changing CID. Transparency and immutability — mandatory trust conditions.
Merkle-Based On-Chain Verification
For projects wanting on-chain rarity verification (e.g., bonuses for top-100 holders):
// Merkle proof rarity rank verification
function verifyRarityRank(
uint256 tokenId,
uint256 rank,
bytes32[] calldata proof
) external view returns (bool) {
bytes32 leaf = keccak256(abi.encodePacked(tokenId, rank));
return MerkleProof.verify(proof, rarityMerkleRoot, leaf);
}
Root generated from full tree {tokenId: rank} and fixed in contract after reveal. User proves their rank without storing 10,000 values on-chain.
API for Aggregators
Rarity Tool, Rarity Sniper, OpenSea — all read metadata from tokenURI(). Important to correctly format attributes:
{
"attributes": [
{"trait_type": "Background", "value": "Gold"},
{"trait_type": "Eyes", "value": "Laser"},
{"display_type": "number", "trait_type": "Rarity Rank", "value": 42},
{"display_type": "number", "trait_type": "Rarity Score", "value": 847.3}
]
}
display_type: "number" allows OpenSea and other marketplaces to show rarity rank as numeric field with sorting.
Typical Development Mistakes
Trait count not considered. Tokens with different number of attributes (some NFTs may not have certain trait_type) get unfair score. Solution: None as separate value with appearance frequency.
Score calculated before final generation. If artist adds new variants after calculation — whole table invalid. Rarity calculated once on final collection, before any changes.
No tiebreaker. Tokens with same score get same rank. Standard approach — tiebreak by tokenId (lower ID = higher rank on equal score).
Development Process
Trait structure analysis (0.5–1 day). Analyze attribute set, frequencies, choose algorithm. If project has partial generation — verify trait distribution correctness (no accidental duplicates, gaps).
Script development (1–2 days). Python pipeline: trait analysis → score calculation → metadata update → IPFS upload. Output: JSON with rarity_score and rarity_rank for each token, CSV for project team, updated metadata files.
On-chain component (1 day if needed). Merkle tree generation, verifier deployment, proof testing.
Marketplace integration (0.5 day). Verify correct display on OpenSea, Blur, LooksRare.
Timeline: 2–3 days for collections up to 10,000 tokens. For larger or non-standard algorithms — up to 5 days. Cost calculated after analyzing collection structure.







