Introducing the NFT Utilities SDK for JavaScript
Small profile pic michiel dec 2022
Jan 16, 2023
by Michiel Mulders
Developer Advocate at Swirlds Labs

The Hedera NFT Utilities SDK for JavaScript is a set of tools and resources developers can use to build NFT-related software or perform NFT-related actions. The goal of the NFT Utilities SDK is to make it easier for builders and artists to implement standard NFT features, such as calculating risk and rarity, or perform NFT tasks, such as verifying NFT metadata against a schema specification like HIP412.

By providing several helpful NFT utilities, we want to support the Hedera NFT community in building faster, more, and better NFT projects. Instead of building your own NFT tooling, you can make use of the building blocks provided by the Hedera NFT Utilities SDK. 

The project aims to evolve the Hedera NFT Utilities JavaScript SDK based on community feedback to make it an excellent tool for NFT creators on the Hedera network.

In this blog post, you'll learn:

  1. What is the Hedera NFT Utilities SDK?
  2. How to install the Hedera NFT Utilities SDK?
  3. Hedera NFT Functionalities
    1. How do the HIP412 metadata validator and local validator work?
    2. How does the risk score calculation work?
    3. How does the rarity score calculation work?
  4. Feedback or Suggestions

What is the Hedera NFT Utilities SDK?

The Hedera NFT Utilities SDK consists of three core functionalities. Here's a quick overview:

  1. Metadata validation
    1. Verify metadata against the HIP412 standard. You can input your own metadata or the metadata of an existing NFT on the Hedera network. It's possible to add custom validation rules or your own metadata standard.
    2. If you want to publish an NFT project, first check the metadata validity against HIP412 for all of your metadata files. You can use the "local metadata validator" to verify all of your metadata files in bulk and print the results.
  2. Rarity score calculation
    Calculate the rarity for an NFT collection locally to make sure the rarity distribution is correct. The rarity score calculation function accepts a path to a folder containing metadata JSON files and calculates the rarity based on the listed "attributes" and their values. The JSON files must follow the HIP412 metadata specification. Once you are happy with the rarity scores and distribution, you can publish the NFT project on the Hedera network.

  3. Risk score calculation
    The risk score functions look at the presence of specific token keys to determine a risk level, and so, a risk score.
    1. Calculate the risk score for an existing NFT on the Hedera network by inputting a "token ID". 
    2. Calculate the risk score for any NFT locally or on-chain by inputting JSON metadata. 

Let's take a look at the details of each package. But first, how do you install the Hedera NFT Utilities SDK?

How to install the Hedera NFT Utilities SDK?

Because the package is created with JavaScript, you can use NPM or Yarn package managers to install the SDK.

Code Snippet Background
npm i -s @hashgraph/nft-utilities

Next, you can import the functionality you need. Let's look at each functionality individually to see what you can do and how it works.

How do the HIP412 metadata validator and local validator work?

The SDK offers multiple validator functions. Let's look at the main function you can use to validate metadata against the HIP412 standard.

1. "validator()"

Code Snippet Background
const { validator, defaultVersion } = require('@hashgraph/nft-utilities');

// Valid metadata instance
const metadataInstance = {
        "creator": "HANGRY BARBOONS",
        "description": "HANGRY BARBOONS are 4,444 unique citizens from the United Hashgraph of Planet Earth. Designed and illustrated by President HANGRY.",
        "format": "none",
        "name": "HANGRY BARBOON #2343",
        "image": "ipfs://QmaHVnnp7qAmGADa3tQfWVNxxZDRmTL5r6jKrAo16mSd5y/2343.png",
        "type": "image/png",
        "properties": { "edition": 2343 },
        "attributes": [
          { "trait_type": "Background", "value": "Yellow" },
          { "trait_type": "Fur", "value": "Gold" },
          { "trait_type": "Clothing", "value": "Floral Jacket" },
          { "trait_type": "Mouth", "value": "Tongue" },
          { "trait_type": "Sing", "value": "None" }
        ]
 }

 const results = validator(metadataInstance); // by default: verifies metadata against [email protected]
 console.log(results);

/* Output:
    { errors: [], warnings: [] }
*/

The validator function accepts stringified metadata and a version number. The version number refers to a specific version of HIP412 you want to verify your metadata against. Currently, you can import the "defaultVersion" parameter, which refers to the latest version of the HIP412 specification (HIP412 v2.0.0).

The validator function outputs both errors and warnings. Errors refer to mistakes against the HIP412 standard, while warnings are related to additional properties on the JSON object not specified by the HIP412 metadata standard. According to the standard, any additional properties should be included in the "properties" object to keep the JSON object tidy and easy to interpret.

2. "localValidation()"

Code Snippet Background
const { localValidation } = require('@hashgraph/nft-utilities');

const absolutePathToFiles = "/Users/myUser/nft-utilities/examples/local-metadata-validator/files";
localValidation(absolutePathToFiles);

/* Output:
        Found 6 for directory: /Users/myUser/nft-utilities/examples/local-metadata-validator/files
        Found 5 files with the .json extension
        { 
            "nft1.json": {"errors":[],"warnings":[]},
            "nft2.json":{"errors":[{"type":"schema","msg":"requires property 'image'","path":"instance"},{"type":"schema","msg":"requires property 'type'","path":"instance"}],"warnings":[]},
            "nft3.json":{"errors":[],"warnings":[]},
            "nft4.json":{"errors":[],"warnings":[]},
            "nft5.json":{"errors":[],"warnings":[]}
        }
 */

The local validator allows you to verify a folder on your machine containing multiple JSON metadata files. For instance, you have created a new NFT project, and you want to make sure all metadata files are correctly formatted according to the HIP412 standard. You can use the function by passing an absolute path to the folder containing the JSON files.

The output format is the same for the "localValidation" function, returning errors and warnings for each file the function finds in the specified folder.

How does the risk score calculation work?

The risk score calculation functions allow you to calculate the risk score for a token based on the token information by looking at the keys that are set for the token. The presence of each key has an associated risk. For instance, the presence of an admin key is one of the highest risks for a token holder. The admin key can update the properties of the token or delete the token.

To calculate the risk score, each key or property receives a "weight". By adding all weights, you get a final risk score for a token. Here's the overview of all the weights.

Code Snippet Background
const defaultWeights = {
  keys: {
    admin_key: 200,
    wipe_key: 200,
    freeze_key: 50,
    supply_key: 20,
    kyc_key: 50,
    pause_key: 50,
    fee_schedule_key: 40
  },
  properties: {
    supply_type_infinite: 20
  }
}

Weight Distribution Explained

First, let's look at the different keys and why they received their respective weight.

  • Admin key (200): Ability to delete token and update token properties, even has the authority to change supply, freeze, pause, wipe, and KYC key. This key almost gives you absolute control over the token and inherently introduces many risks.
  • Wipe key (200): Ability to wipe the token balance of an account.
  • Freeze key (50): Ability to sign to freeze or unfreeze an account for token transactions. Frozen accounts can't transfer the specific token.
  • KYC key (50): Ability to grant or revoke KYC of an account for the token's transactions. If revoked, the account can't transfer tokens.
  • Pause key (50): Ability to pause a token. Pausing a token prevents the token from participating in all transactions.
  • Fee schedule key (40): Ability to change the token's custom fee schedule. For instance, increasing the royalty fee for an NFT to 99%, whereas the industry standard sits around 5-10%. It's not possible to increase the royalty fee above 100%.
  • Supply key (20): Ability to change the total supply and is authorized to mint or burn tokens. The risk here is that the supply key can create artificial inflation or deflation. However, there's no risk when the supply type is set to finite, and the max supply has been reached. In this situation, it's not possible to further dilute the NFT collection.

Next, let's look at property-specific weights:

  • Supply type equals "INFINITE" and supply key is set (20): If the supply type is set to infinite, it means that the supply key, when set, can mint thousands of new NFTs, diluting the project's value.
  • Supply type is "FINITE" and max supply equals total supply (-20): Reduce the total risk score by 20 because there's no risk when the maximum supply for a project is reached with a finite supply type.

How to calculate the risk level?

Next, the risk level is determined based on the total risk score. For instance, a token with a risk score of 199 will receive a risk level of "MEDIUM". Here's the distribution of risk levels.

Code Snippet Background
const defaultRiskLevels = {
    NORISK: 0,
    LOW: 40,
    MEDIUM: 199,
    HIGH: 2000
};

There are two functions you can use to calculate the risk score. You can either input the token data yourself or provide a token ID. When you provide a token ID, the function will look up the token metadata on the mainnet and calculate the risk score and level.

Code Snippet Background
const { calculateRiskScoreFromData, calculateRiskScoreFromTokenId } = require('@hashgraph/nft-utilities');

const results = await calculateRiskScoreFromTokenId("0.0.1270555");
console.log(results);

/* Output:
    { riskScore: 20, riskLevel: 'LOW' }
*/

How does the rarity score calculation work?

The "calculateRarity" function allows you to calculate the rarity of a folder on your machine containing JSON metadata files for an NFT collection. The function looks at the traits listed in the "attributes" property and their respective values to calculate the rarity score for each NFT (metadata file).

This package uses the trait normalization rarity scoring model because it's the fairest model to calculate rarity. The model works by dividing the number one by the division of the number of NFTs with a specific trait value and the number of NFTs with the most common trait value for that trait. Here's the formula:

Code Snippet Background
1 / (# of NFTs with trait value / # of NFTs with most common trait value)

Import the function and pass it an absolute path to the folder containing JSON metadata files.

Code Snippet Background
const { calculateRarity } = require('@hashgraph/nft-utilities');

const absolutePathToFiles = "/Users/myUser/nft-utilities/examples/rarity-score-calculation/files";
const results = calculateRarity(absolutePathToFiles);
console.log(results);

The function will output the rarity for each file. The output looks like this.

Code Snippet Background
[
    { rarity: '5.50', NFT: 1, filename: 'nft1.json' },
    { rarity: '6.00', NFT: 2, filename: 'nft2.json' },
    { rarity: '5.50', NFT: 3, filename: 'nft3.json' },
    { rarity: '5.50', NFT: 4, filename: 'nft4.json' },
    { rarity: '11.50', NFT: 5, filename: 'nft5.json' }
]

Feedback or Suggestions?

If you have feature requests or want to submit feedback, please open an issue on the GitHub repository for the project.