@hashgraph/nft-utilities
Advanced tools
Comparing version 2.2.0 to 2.3.0
import { Validator, defaultSchemaVersion } from './validator'; | ||
import localValidation from './local-validation'; | ||
import { defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel } from './risk'; | ||
import { calculateRarity } from './rarity'; | ||
import { calculateRarity, calculateRarityFromData } from './rarity'; | ||
import { Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema } from './types/validator.module'; | ||
import { NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult } from './types/rarity.module'; | ||
import { WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult } from './types/risk.module'; | ||
export { Validator, defaultSchemaVersion, localValidation, defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel, calculateRarity, Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema, NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult, WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult, }; | ||
export { Validator, defaultSchemaVersion, localValidation, defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel, calculateRarity, calculateRarityFromData, Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema, NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult, WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult, }; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateRarity = exports.calculateRiskLevel = exports.calculateRiskScoreFromTokenId = exports.calculateRiskScoreFromData = exports.defaultRiskLevels = exports.defaultWeights = exports.localValidation = exports.defaultSchemaVersion = exports.Validator = void 0; | ||
exports.calculateRarityFromData = exports.calculateRarity = exports.calculateRiskLevel = exports.calculateRiskScoreFromTokenId = exports.calculateRiskScoreFromData = exports.defaultRiskLevels = exports.defaultWeights = exports.localValidation = exports.defaultSchemaVersion = exports.Validator = void 0; | ||
/*- | ||
@@ -40,1 +40,2 @@ * | ||
Object.defineProperty(exports, "calculateRarity", { enumerable: true, get: function () { return rarity_1.calculateRarity; } }); | ||
Object.defineProperty(exports, "calculateRarityFromData", { enumerable: true, get: function () { return rarity_1.calculateRarityFromData; } }); |
@@ -20,2 +20,3 @@ /** | ||
import { RarityResult } from '../types/rarity.module'; | ||
import { Attribute } from '../types/validator.module'; | ||
/** | ||
@@ -27,2 +28,10 @@ * | ||
declare const calculateRarity: (dir: string) => RarityResult[]; | ||
export { calculateRarity }; | ||
/** | ||
* | ||
* @param {Array<Object>} metadataArray Array of JSON objects for rarity calculation | ||
* @return {RarityResult[]} Array of objects with rarity information for each NFT | ||
*/ | ||
declare const calculateRarityFromData: (metadataArray: { | ||
attributes: Attribute[]; | ||
}[]) => RarityResult[]; | ||
export { calculateRarity, calculateRarityFromData }; |
@@ -21,3 +21,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateRarity = void 0; | ||
exports.calculateRarityFromData = exports.calculateRarity = void 0; | ||
const files_1 = require("../helpers/files"); | ||
@@ -113,1 +113,75 @@ /** | ||
}; | ||
/** | ||
* @param {NFTFile[]} files Array of NFTFile objects | ||
* @return {AttributeConfig[]} Array of objects with attribute information | ||
* */ | ||
const getAttributeMapData = (metadataArray) => { | ||
const attributesMap = []; | ||
metadataArray.forEach((metadata) => { | ||
if (!metadata.attributes) | ||
throw new Error(`Attributes not found in object ${JSON.stringify(metadata)}. Please ensure that your metadata file is valid.`); | ||
metadata.attributes.forEach((attribute) => { | ||
const matchedAttributeIndex = attributesMap.findIndex((attributeObject) => attribute.trait_type === attributeObject.trait_type); | ||
if (matchedAttributeIndex !== -1) { | ||
const matchedValueIndex = attributesMap[matchedAttributeIndex].values.findIndex((valueObject) => valueObject.value === attribute.value); | ||
if (matchedValueIndex !== -1) { | ||
attributesMap[matchedAttributeIndex].values[matchedValueIndex] | ||
.count++; | ||
} | ||
else { | ||
attributesMap[matchedAttributeIndex].values.push({ | ||
value: attribute.value.toString(), | ||
count: 1, | ||
}); | ||
} | ||
} | ||
else { | ||
attributesMap.push({ | ||
trait_type: attribute.trait_type, | ||
values: [{ value: attribute.value.toString(), count: 1 }], | ||
}); | ||
} | ||
}); | ||
}); | ||
return attributesMap; | ||
}; | ||
/** | ||
* | ||
* @param {Array<Object>} metadataArray Array of JSON objects for rarity calculation | ||
* @return {RarityResult[]} Array of objects with rarity information for each NFT | ||
*/ | ||
const calculateRarityFromData = (metadataArray) => { | ||
const attributesMap = getAttributeMapData(metadataArray); // todo replace by proper function | ||
const normalizedRarities = []; | ||
let normalizedCount = 1; | ||
metadataArray.forEach((metadata) => { | ||
const traitRarities = []; | ||
metadata.attributes.forEach((NFTAttribute) => { | ||
const attributeConfigObject = attributesMap.find((attribute) => attribute.trait_type === NFTAttribute.trait_type); | ||
if (!attributeConfigObject) | ||
throw new Error(`Attribute ${NFTAttribute.trait_type} not found in attributes map`); | ||
const NFTsWithTrait = attributeConfigObject.values.find((valueObject) => valueObject.value === NFTAttribute.value); | ||
const mostCommonTrait = attributeConfigObject.values.reduce((prev, current) => (prev.count > current.count ? prev : current)); | ||
const traitRarity = 1 / ((NFTsWithTrait === null || NFTsWithTrait === void 0 ? void 0 : NFTsWithTrait.count) / mostCommonTrait.count); | ||
traitRarities.push({ | ||
trait: NFTAttribute.trait_type, | ||
value: NFTAttribute.value, | ||
rarity: traitRarity, | ||
}); | ||
}); | ||
const totalRarity = traitRarities.reduce((prev, current) => prev + current.rarity, 0); | ||
const attributeContributions = traitRarities.map(traitRarity => ({ | ||
trait: traitRarity.trait, | ||
value: traitRarity.value, | ||
contribution: (traitRarity.rarity / totalRarity * 100).toFixed(2), | ||
})); | ||
normalizedRarities.push({ | ||
attributeContributions: attributeContributions, | ||
totalRarity: totalRarity.toFixed(2), | ||
NFT: normalizedCount, | ||
}); | ||
normalizedCount++; | ||
}); | ||
return normalizedRarities; | ||
}; | ||
exports.calculateRarityFromData = calculateRarityFromData; |
@@ -47,3 +47,3 @@ /*- | ||
NFT: number; | ||
filename: string; | ||
filename?: string; | ||
} |
import { Validator, defaultSchemaVersion } from './validator'; | ||
import localValidation from './local-validation'; | ||
import { defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel } from './risk'; | ||
import { calculateRarity } from './rarity'; | ||
import { calculateRarity, calculateRarityFromData } from './rarity'; | ||
import { Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema } from './types/validator.module'; | ||
import { NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult } from './types/rarity.module'; | ||
import { WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult } from './types/risk.module'; | ||
export { Validator, defaultSchemaVersion, localValidation, defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel, calculateRarity, Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema, NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult, WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult, }; | ||
export { Validator, defaultSchemaVersion, localValidation, defaultWeights, defaultRiskLevels, calculateRiskScoreFromData, calculateRiskScoreFromTokenId, calculateRiskLevel, calculateRarity, calculateRarityFromData, Attribute, Localization, File, Instance, Error, Problem, ValidationResult, Schema, NFTFile, NFTAttribute, ValueObject, AttributeConfig, RarityResult, WeightKeys, WeightProperties, Weights, KeyTypes, RiskLevels, RiskLevelTypes, Metadata, RiskResult, }; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateRarity = exports.calculateRiskLevel = exports.calculateRiskScoreFromTokenId = exports.calculateRiskScoreFromData = exports.defaultRiskLevels = exports.defaultWeights = exports.localValidation = exports.defaultSchemaVersion = exports.Validator = void 0; | ||
exports.calculateRarityFromData = exports.calculateRarity = exports.calculateRiskLevel = exports.calculateRiskScoreFromTokenId = exports.calculateRiskScoreFromData = exports.defaultRiskLevels = exports.defaultWeights = exports.localValidation = exports.defaultSchemaVersion = exports.Validator = void 0; | ||
/*- | ||
@@ -40,1 +40,2 @@ * | ||
Object.defineProperty(exports, "calculateRarity", { enumerable: true, get: function () { return rarity_1.calculateRarity; } }); | ||
Object.defineProperty(exports, "calculateRarityFromData", { enumerable: true, get: function () { return rarity_1.calculateRarityFromData; } }); |
@@ -20,2 +20,3 @@ /** | ||
import { RarityResult } from '../types/rarity.module'; | ||
import { Attribute } from '../types/validator.module'; | ||
/** | ||
@@ -27,2 +28,10 @@ * | ||
declare const calculateRarity: (dir: string) => RarityResult[]; | ||
export { calculateRarity }; | ||
/** | ||
* | ||
* @param {Array<Object>} metadataArray Array of JSON objects for rarity calculation | ||
* @return {RarityResult[]} Array of objects with rarity information for each NFT | ||
*/ | ||
declare const calculateRarityFromData: (metadataArray: { | ||
attributes: Attribute[]; | ||
}[]) => RarityResult[]; | ||
export { calculateRarity, calculateRarityFromData }; |
@@ -21,3 +21,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.calculateRarity = void 0; | ||
exports.calculateRarityFromData = exports.calculateRarity = void 0; | ||
const files_1 = require("../helpers/files"); | ||
@@ -113,1 +113,75 @@ /** | ||
}; | ||
/** | ||
* @param {NFTFile[]} files Array of NFTFile objects | ||
* @return {AttributeConfig[]} Array of objects with attribute information | ||
* */ | ||
const getAttributeMapData = (metadataArray) => { | ||
const attributesMap = []; | ||
metadataArray.forEach((metadata) => { | ||
if (!metadata.attributes) | ||
throw new Error(`Attributes not found in object ${JSON.stringify(metadata)}. Please ensure that your metadata file is valid.`); | ||
metadata.attributes.forEach((attribute) => { | ||
const matchedAttributeIndex = attributesMap.findIndex((attributeObject) => attribute.trait_type === attributeObject.trait_type); | ||
if (matchedAttributeIndex !== -1) { | ||
const matchedValueIndex = attributesMap[matchedAttributeIndex].values.findIndex((valueObject) => valueObject.value === attribute.value); | ||
if (matchedValueIndex !== -1) { | ||
attributesMap[matchedAttributeIndex].values[matchedValueIndex] | ||
.count++; | ||
} | ||
else { | ||
attributesMap[matchedAttributeIndex].values.push({ | ||
value: attribute.value.toString(), | ||
count: 1, | ||
}); | ||
} | ||
} | ||
else { | ||
attributesMap.push({ | ||
trait_type: attribute.trait_type, | ||
values: [{ value: attribute.value.toString(), count: 1 }], | ||
}); | ||
} | ||
}); | ||
}); | ||
return attributesMap; | ||
}; | ||
/** | ||
* | ||
* @param {Array<Object>} metadataArray Array of JSON objects for rarity calculation | ||
* @return {RarityResult[]} Array of objects with rarity information for each NFT | ||
*/ | ||
const calculateRarityFromData = (metadataArray) => { | ||
const attributesMap = getAttributeMapData(metadataArray); // todo replace by proper function | ||
const normalizedRarities = []; | ||
let normalizedCount = 1; | ||
metadataArray.forEach((metadata) => { | ||
const traitRarities = []; | ||
metadata.attributes.forEach((NFTAttribute) => { | ||
const attributeConfigObject = attributesMap.find((attribute) => attribute.trait_type === NFTAttribute.trait_type); | ||
if (!attributeConfigObject) | ||
throw new Error(`Attribute ${NFTAttribute.trait_type} not found in attributes map`); | ||
const NFTsWithTrait = attributeConfigObject.values.find((valueObject) => valueObject.value === NFTAttribute.value); | ||
const mostCommonTrait = attributeConfigObject.values.reduce((prev, current) => (prev.count > current.count ? prev : current)); | ||
const traitRarity = 1 / ((NFTsWithTrait === null || NFTsWithTrait === void 0 ? void 0 : NFTsWithTrait.count) / mostCommonTrait.count); | ||
traitRarities.push({ | ||
trait: NFTAttribute.trait_type, | ||
value: NFTAttribute.value, | ||
rarity: traitRarity, | ||
}); | ||
}); | ||
const totalRarity = traitRarities.reduce((prev, current) => prev + current.rarity, 0); | ||
const attributeContributions = traitRarities.map(traitRarity => ({ | ||
trait: traitRarity.trait, | ||
value: traitRarity.value, | ||
contribution: (traitRarity.rarity / totalRarity * 100).toFixed(2), | ||
})); | ||
normalizedRarities.push({ | ||
attributeContributions: attributeContributions, | ||
totalRarity: totalRarity.toFixed(2), | ||
NFT: normalizedCount, | ||
}); | ||
normalizedCount++; | ||
}); | ||
return normalizedRarities; | ||
}; | ||
exports.calculateRarityFromData = calculateRarityFromData; |
@@ -47,3 +47,3 @@ /*- | ||
NFT: number; | ||
filename: string; | ||
filename?: string; | ||
} |
{ | ||
"name": "@hashgraph/nft-utilities", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "NFT Utilities for Hedera Hashgraph", | ||
@@ -5,0 +5,0 @@ "author": "Michiel Mulders", |
@@ -389,2 +389,23 @@ <div align="center"> | ||
You can also avoid having to load data from files by using the `calculateRarityFromData` function. | ||
```js | ||
const NFTdata = [ | ||
{ | ||
"name": "HANGRY BARBOON #2343", | ||
"image": "ipfs://QmaHVnnp7qAmGADa3tQfWVNxxZDRmTL5r6jKrAo16mSd5y/2343.png", | ||
"type": "image/png", | ||
"attributes": [ | ||
{ "trait_type": "Background", "value": "Yellow" }, | ||
{ "trait_type": "Fur", "value": "Silver" }, | ||
{ "trait_type": "Clothing", "value": "Herbal Jacket" }, | ||
{ "trait_type": "Mouth", "value": "Smile" }, | ||
{ "trait_type": "Sing", "value": "Sing" } | ||
] | ||
} | ||
] | ||
const results = calculateRarityFromData(NFTdata); | ||
``` | ||
According to token metadata JSON schema V2, the `calculateRarity` function only looks at objects in the `attributes` property that use the following format: | ||
@@ -394,3 +415,7 @@ | ||
{ "trait_type": "Background", "value": "Yellow" } | ||
OR | ||
``` | ||
It does not take into account attributes with the `display_type` property set, like this: | ||
``` | ||
{ "trait_type": "Background", "value": 10, "display_type": "percentage" } | ||
@@ -405,3 +430,3 @@ ``` | ||
[ | ||
{ "rarity": "<string> rarity score", "NFT": "<nubmer> NFT number", "filename": "<string>" }, | ||
{ "rarity": "<string> rarity score", "NFT": "<nubmer> NFT number", "filename": "<string optional>" }, | ||
... | ||
@@ -453,3 +478,5 @@ ] | ||
See: **[/examples/rarity-score-calculation/index.js](https://github.com/hashgraph/hedera-nft-utilities/tree/main/examples/rarity-score-calculation)** | ||
See: | ||
- **[/examples/rarity-score-calculation/rarity-from-files.js](https://github.com/hashgraph/hedera-nft-utilities/tree/main/examples/rarity-score-calculation)** | ||
- **[/examples/rarity-score-calculation/rarity-from-data.js](https://github.com/hashgraph/hedera-nft-utilities/tree/main/examples/rarity-score-calculation)** | ||
@@ -456,0 +483,0 @@ ## Questions or Improvement Proposals |
150830
3070
499