Minting a tree for compressed NFTs
Tags: solana, javascript, node
Published: 2023-03-23 15:15:48

If you are still NFT Twitter adjacent, you've seen a firehouse of tweets about compressed NFTs. Just like with Candy Machine V1, they are all the rage. Today we are focused on step one of minting a compressed NFT, `minting a tree on-chain`.

I've found a few great explanations on what compressed NFTs are:

![Compression](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4adz78vfdo8r01k8t95s.png)

Alchemy's: [What are compressed NFTs?](https://www.alchemy.com/overviews/compressed-nfts)

Metaplex's: [Expanding Digital Assets with Compression for NFTs](https://www.metaplex.com/posts/expanding-digital-assets-with-compression-for-nfts)


The main goal of compressed NFTs is to dramatically decrease the cost of minting to greatly widen the adoption of Solana NFTs.

When working with toy Solana projects, I've found that using a Next.js app is the best way to get started quickly. In my experience, you need a node server to work with Metaplex code. With the api folder, you can get a lot done without having to worry about compatibility and code splitting.

## Getting started

We need to install relevant dependencies

```bash
npm install @solana/spl-account-compression @solana/web3.js @metaplex-foundation/mpl-bubblegum @mintee/wrapped-connection
```

The first part of working with compressed NFTs is familiarizing yourself with the connection wrapper. If you follow any example, you will see that it extends the normal `@solana/web3.js` Connection class with some helpers around Keypairs and compressed NFTs. You pass in your RPC URL and a keypair to sign/pay for transactions.



```typescript
import WrappedConnection from "@mintee/wrapped-connection"
import {
  Keypair,
  PublicKey,
  SystemProgram,
  Transaction,
  sendAndConfirmTransaction,
} from "@solana/web3.js";


const keypair = new Keypair()
const connectionWrapper = WrappedConnection.getConnectionWrapper("https://api.mainnet-beta.solana.com", keypair);
```

We are going to define a function called `mintTree()`. When minting an account to hold your tree, you need to define the depth and buffer size. 2^{DEPTH} is the capacity of the tree.

When working with Solana, you have to calculate how large the account size needs to be upfront. When working with compressed NFTs, you have a higher up-front cost, but each mint is cheaper.

![How do they work?](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0cwjlp39wl9swkxt5kp.png)

```typescript
export async function mintTree() {
  const payer = connectionWrapper.payer.publicKey;
  const treeKeypair = new Keypair();


  const maxDepth = 14
  const maxBufferSize = 64

// create account size based on number of NFTs
  const space = getConcurrentMerkleTreeAccountSize(maxDepth, maxBufferSize);
// get tree authority 
  const [treeAuthority, _bump] = await PublicKey.findProgramAddressSync(
    [treeKeypair.publicKey.toBuffer()],
    BUBBLEGUM_PROGRAM_ID
  );
```

Now that we have created the instructions for minting an account we need to write the transactions for allocating the account and creating the tree. 

```typescript
const createTreeIx = createCreateTreeInstruction(
    {
      merkleTree: treeKeypair.publicKey,
      treeAuthority,
      treeCreator: payer,
      payer,
      logWrapper: SPL_NOOP_PROGRAM_ID,
      compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
    },
    {
      maxBufferSize,
      maxDepth,
      public: false,
    },
    BUBBLEGUM_PROGRAM_ID
  );


  let tx = new Transaction().add(allocTreeIx).add(createTreeIx);
  tx.feePayer = payer;
```

After sending the transaction we will get back an assetId. The assetId is a PDA of Bubblegum. It the equivalent of a token address. This is where compressed NFTs get controversial. They are a pretty unique RPC extension, so if you mint a compressed NFT on Helius per se, you have to use that RPC to get back the info. 

```typescript
try {
    await sendAndConfirmTransaction(
      connectionWrapper,
      tx,
      [treeKeypair, connectionWrapper.payer],
      {
        commitment: "confirmed",
        skipPreflight: true,
      }
    );
    
    console.log(
      "Successfull created merkle tree for account: " + treeKeypair.publicKey
    );

    return treeKeypair.publicKey;


  } catch (e) {
    console.error("Failed to create merkle tree: ", e);
    throw e;
  }
}
```