Skip to content

Create a cNFT collection of your own profile picture and social links as metadata and airdrop it to other fellows.

Notifications You must be signed in to change notification settings

Laugharne/ssf_s8_exo

Repository files navigation

Compressed NFTs

Exercise: Create a cNFT collection of your own profile picture and social links as metadata and airdrop it to other fellows.

Topics in this exercise:

  • bun
  • TypeScript
  • Merkle Tree
  • Compressed NFT
  • Metaplex
  • Bubblegum

Table of contents:

Installation

Clone the repo:

git clone https://github.com/Laugharne/ssf_s8_exo.git

Install bun if needed

curl -fsSL https://bun.sh/install | bash

bun --help

To install dependencies:

bun install

To run:

bun run index.ts

This project was created using bun init in bun v1.1.20. Bun is a fast all-in-one JavaScript runtime.

If you occure this problem: bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?), resolve it by running the following command npm rebuild

Overview

Compressed NFTs on Solana are a more scalable, cost-efficient way to mint and manage NFTs, using off-chain storage with on-chain proofs via Merkle trees. Here's how it works:

  • Merkle Tree: A data structure that stores compressed proofs on-chain for efficient verification. Created using the createTree() call from metaplex/bubblegum, it allows for a compact, secure representation of many NFTs.

  • NFT Collection: Defines a group of related NFTs. Created with the createNft() call from metaplex, it sets up metadata and ownership details for the collection.

  • Minting: The process of adding new NFTs to a collection. This is done with mintToCollectionV1() from metaplex/bubblegum, allowing efficient addition of multiple NFTs to a collection using the Merkle tree structure.

Compressed NFTs enable large-scale minting with reduced costs, making them ideal for high-volume use cases like gaming and digital collectibles.

Settings

Update the different fields and values in the following files:

".env" file

  1. Specify wich kind of environnment you will use production or developement
  2. Set your Helius API key (Dashboard | Helius) if you have one, else stay with the current RPC URL of Solana...
NODE_ENV=developement
SOLANA_MAINNET_RPC_URL=https://rpc.helius.xyz/?api-key=<HELIUS_API_KEY>
SOLANA_DEVNET_RPC_URL=https://devnet.helius-rpc.com/?api-key=<HELIUS_API_KEY>

"key.json" file

Put "key.json" file at project root, this will be the payer wallet (Format of the json file generated by solana-keygen grind command)

"config.ts" file

There is several fields to set:

  • MERKLE_MAX_DEPTH : parameter specifies the maximum height of the Merkle tree, which dictates the total number of NFTs (or leaves) it can hold. To accommodate 37 NFTs, we need the smallest maxDepth.

    Let's compute this:

    • 2^5 = 32 (too small)
    • 2^6 = 64 (sufficient)

    So, the smallest depth that can hold 37 NFTs seems to be maxDepth = 6.

    But there's only some specific couple of values for MERKLE_MAX_DEPTH and MERKLE_MAX_BUFFER_SIZE who seems to be authorized for the Merkle Tree !

    See: solana-program-library/account-compression/programs/account-compression/src/state/concurrent_merkle_tree_header.rs)

    /// Initialization parameters for an SPL ConcurrentMerkleTree.
    ///
    /// Only the following permutations are valid:
    ///
    /// | max_depth | max_buffer_size       |
    /// | --------- | --------------------- |
    /// | 14        | (64, 256, 1024, 2048) |
    /// | 20        | (64, 256, 1024, 2048) |
    /// | 24        | (64, 256, 512, 1024, 2048) |
    /// | 26        | (64, 256, 512, 1024, 2048) |
    /// | 30        | (512, 1024, 2048) |
    

    So i choose the following couple of values (14, 64) for MERKLE_MAX_DEPTH and MERKLE_MAX_BUFFER_SIZE !

  • Two off-chain JSON file metadata, URL to set (see section "Metadata" below)

    • METADATA_COLLECTION_URL for the collection
    • METADATA_ITEM_URL for each items minted (can be updated between each one)
  • IMAGE_URL : Image URL of the cNFT

  • Name, symbol and description of the collection

  • FEE_PERCENT : The royalties shared by the creators in basis points (550 means 5.5% royalties)

  • EXTERNAL_URL : URI pointing to an external URL defining the asset (the creator's website for example)

export const MERKLE_MAX_DEPTH       = 14;
export const MERKLE_MAX_BUFFER_SIZE = 64;

export const METADATA_COLLECTION_URL = "https://laugharne.github.io/cnft_metadata.json";
export const METADATA_ITEM_URL       = "https://laugharne.github.io/cnft_item_metadata.json";
export const IMAGE_URL               = "https://laugharne.github.io/logo.png";

export const COLLECTION_NAME        = 'Solana Summer Fellowship 2024'
export const COLLECTION_SYMBOL      = 'SSF24'
export const COLLECTION_DESCRIPTION = 'Solana Summer Fellowship 2024 cNFT collection from Laugharne'
export const FEE_PERCENT            = 0
export const EXTERNAL_URL           = 'https://laugharne.github.io'

export const NFT_ITEM_NAME      = 'Laugharne Limited Edition'

In normal production environment, the off-chain data are hosted on services like NFT.storage or ipfs !

For this exercise i choose to host them on a GitHub account...

"fellow.csv" file

Prepare a list of Solana wallet addresses for the airdrop, in a CSV file.

The CSV file should contain a single column with the header "address", and each row should contain a valid Solana wallet address.

Example :

address
8zN3Wu9K5YX7xMdSXP6Q5bX6FN2h4pLJhxJWpT9uRT7Y
6bJ8eY2F2H1PxNqv4W3zV4XShGqT7G7Hk1Fm5XxgHJ2k
2yL9vR4fJDv7QZCtw8Zw1ND3y5mQeW2Kxqf8nB7nqTXY
5ZcR1WrVn8F8y1mQ5tBfjMkpD2xxVz2JQ5pJMezcvz8e

Usage

After set all required parameters and data as seen in previous section.

We can run the processus...

  1. At first we create the cNFT collection : bun run 1_createNFTCollection
  2. Then creating the Merkle Tree : bun run 2_createMerkleTree
  3. This program read a CSV file with all the addresses to airdrop : bun run 3_mintCNFT.ts

Each call generate a "file" in data directory, to keep a trace of the processus These data are used cross programs too...

Tree repository

.
├── .gitignore
├── 1_createNFTCollection.ts
├── 2_createMerkleTree.ts
├── 3_mintCNFT.ts
├── README.md
├── assets
│   ├── 2024-09-10-16-22-42.png
│   ├── 2024-09-12-17-34-38.png
│   └── julie.png
├── bun.lockb
├── cnft_item_metadata.json
├── cnft_metadata.json
├── config.ts
├── data
│   ├── collectionImageUri.txt
│   ├── collectionJsonUri.txt
│   ├── collectionMintDevnet.txt
│   ├── collectionMintMainnet.txt
│   ├── merkleTreeDevnet.txt
│   ├── merkleTreeMainnet.txt
│   ├── nftItemJsonUri.txt
│   ├── nftItemMintDevnet.txt
│   └── nftItemMintMainnet.txt
├── index.ts
├── package-lock.json
├── package.json
├── tsconfig.json
└── utils.ts

Metadata

cNFT Collection Metadata

{
    "name"                   : "Solana Summer Fellowship 2024",
    "symbol"                 : "SSF24",
    "description"            : "Solana Summer Fellowship 2024 cNFT collection from Laugharne",
    "seller_fee_basis_points": 0,
    "image"                  : "https://laugharne.github.io/logo.png",
    "external_url"           : "https://laugharne.github.io/",
    "attributes"             : [],
    "collection"             : {
        "name"  : "Laugharne cNFTs Collection #2",
        "family": "Laugharne cNFTs"
    },
    "properties": {
        "files": [
            {
                "uri" : "https://laugharne.github.io/logo.png",
                "type": "image/png"
            }
        ],
        "category": "image"
    }
}

cNFT item metadata

{
    "name"                   : "Solana Summer Fellowship 2024",
    "symbol"                 : "SSF24",
    "description"            : "Solana Summer Fellowship 2024 cNFT collection from Laugharne",
    "seller_fee_basis_points": 0,
    "image"                  : "https://laugharne.github.io/logo.png",
    "external_url"           : "https://laugharne.github.io/",
    "attributes"             : [
        {
            "trait_type": "Program",
            "value"     : "Solana Summer Fellowship"
        },
        {
            "trait_type": "Cohorte",
            "value"     : "Summer 2024"
        },
        {
            "trait_type": "Mentor #1",
            "value"     : "Kunal Bagaria"
        },
        {
            "trait_type": "Mentor #2",
            "value"     : "Syed Aabis Akhtar"
        },
        {
            "trait_type": "Status",
            "value"     : "Yeah!"
        },
        {
            "trait_type": "Mint date",
            "value"     : "2024-09-12"
        }
    ],
    "collection": {
        "name"  : "Laugharne cNFTs Collection #2",
        "family": "Laugharne cNFTs"
    },
    "properties": {
      "files": [
        {
            "uri" : "https://laugharne.github.io/logo.png",
            "type": "image/png"
        }
      ],
      "category": "image"
    },
    "creators": {
        "address" : "9BbWp6tcX9MEGSUEpNXfspYxYsWCxE9FgRkAc3RpftkT",
        "verified": false,
        "share"   : 100
    }
  }

Some references about metadata

Metaplex dependancies versions

"@metaplex-foundation/mpl-bubblegum":            "^1.0.1",
"@metaplex-foundation/mpl-token-metadata":       "^3.0.0",
"@metaplex-foundation/umi-bundle-defaults":      "^0.8.9",
"@metaplex-foundation/umi-uploader-nft-storage": "^0.8.9",

Some traces of my first attempts to mint cNFTs

Addresses, Accounts, TX and scrrenshot:

Resources

From fellowship:

Metaplex/Bubblegum:

Solandy videos:

Misc:


About

Create a cNFT collection of your own profile picture and social links as metadata and airdrop it to other fellows.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published