README.md

# BubblegumNifs

## Introduction

BubblegumNifs is an Elixir library that provides a native interface to Solana's Metaplex Bubblegum program for working with compressed NFTs. Compressed NFTs allow for significant cost savings and improved scalability compared to traditional Solana NFTs by utilizing state compression techniques.

This library provides Elixir developers with the ability to:
- Create merkle trees for storing compressed NFTs
- Mint compressed NFTs as standalone assets or as part of collections
- Transfer compressed NFTs between wallets

The implementation uses Rustler to create Native Implemented Functions (NIFs) that directly interact with Solana's programs through Rust, providing efficient and type-safe operations.

## Installation

Add `bubblegum_nifs` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:bubblegum_nifs, "~> 0.1.0"}
  ]
end
```

## Architecture

BubblegumNifs follows a layered architecture:

1. **Elixir Interface Layer**: High-level, developer-friendly functions
2. **Elixir-Rust Bridge**: Rustler NIF functions that communicate with Rust code
3. **Rust Core Layer**: Direct interaction with Solana programs through native Rust
4. **Solana RPC Layer**: Communication with Solana blockchain

## Data Structures

### KeyPairInfo

Represents a Solana keypair:

```elixir
%BubblegumNifs.KeyPairInfo{
  pubkey: String.t(),  # Base58 encoded public key
  secret: binary()     # Raw secret key bytes
}
```

### MetadataArgs

NFT metadata for minting:

```elixir
%BubblegumNifs.MetadataArgs{
  name: String.t(),                     # NFT name
  symbol: String.t(),                   # NFT symbol
  uri: String.t(),                      # JSON metadata URI
  seller_fee_basis_points: integer(),   # Royalty amount (e.g., 500 = 5%)
  primary_sale_happened: boolean(),     # Whether primary sale occurred
  is_mutable: boolean(),                # Whether metadata can be changed
  edition_nonce: integer() | nil,       # Edition nonce if applicable
  creators: [Creator.t()],              # List of creators
  collection: Collection.t() | nil,     # Optional collection details
  uses: Uses.t() | nil                  # Optional uses configuration
}
```

### Creator

Represents an NFT creator:

```elixir
%BubblegumNifs.Creator{
  address: String.t(),  # Creator's wallet address
  verified: boolean(),  # Whether creator signature is verified
  share: integer()      # Percentage share of royalties (0-100)
}
```

### Collection

Collection information for the NFT:

```elixir
%BubblegumNifs.Collection{
  verified: boolean(),  # Whether collection verification is completed
  key: String.t()       # Collection NFT mint address
}
```

### Uses

Defines how an NFT can be "used" (consumed):

```elixir
%BubblegumNifs.Uses{
  use_method: integer(),   # 0: Burn, 1: Multiple, 2: Single
  remaining: integer(),    # Uses remaining
  total: integer()         # Total allowed uses
}
```

### Transaction

Represents a serialized Solana transaction:

```elixir
%BubblegumNifs.Transaction{
  message: binary(),       # Serialized transaction message
  signatures: [binary()]   # List of transaction signatures
}
```

## Core Functions

### Creating a Merkle Tree

Before minting compressed NFTs, you must create a merkle tree to store them.

```elixir
# Calculate size and rent for a merkle tree
tree_size = BubblegumNifs.MerkleTree.get_merkle_tree_size(max_depth, max_buffer_size)
{:ok, rent} = BubblegumNifs.SolanaRpc.get_mint_rent(client, tree_size)

# Generate a new keypair for the tree
merkle_tree_keypair = BubblegumNifs.Native.generate_keypair()

# Create the tree
{:ok, tx} = BubblegumNifs.Native.create_tree_config_ix(
  payer_keypair,
  merkle_tree_keypair,
  max_depth,
  max_buffer_size,
  recent_blockhash,
  true,  # public
  rent,
  tree_size
)

# Send transaction
{:ok, signature} = BubblegumNifs.SolanaRpc.send_transaction(client, tx)
```

### Minting a Compressed NFT

To mint a new compressed NFT:

```elixir
# Prepare metadata
metadata = %BubblegumNifs.MetadataArgs{
  name: "My Compressed NFT",
  symbol: "CNFT",
  uri: "https://arweave.net/metadata.json",
  seller_fee_basis_points: 500,  # 5%
  primary_sale_happened: false,
  is_mutable: true,
  creators: [
    %BubblegumNifs.Creator{
      address: payer_keypair.pubkey,
      verified: true,
      share: 100
    }
  ],
  collection: nil,
  uses: nil
}

# Get tree authority
tree_authority = BubblegumNifs.Native.get_tree_authority_pda_address(merkle_tree_pubkey)

# Create mint transaction
{:ok, tx} = BubblegumNifs.Native.mint_v1_ix(
  tree_authority,
  recipient_pubkey,
  recipient_pubkey,  # delegate is same as owner initially
  merkle_tree_pubkey,
  payer_keypair,
  metadata,
  recent_blockhash
)

# Send transaction
{:ok, signature} = BubblegumNifs.SolanaRpc.send_transaction(client, tx)
```

### Minting to a Collection

To mint a compressed NFT as part of a collection:

```elixir
# Include collection in metadata
metadata = %BubblegumNifs.MetadataArgs{
  # ... other metadata fields
  collection: %BubblegumNifs.Collection{
    verified: false,  # Will be verified by the transaction
    key: collection_mint_pubkey
  }
}

# Create mint transaction
{:ok, tx} = BubblegumNifs.Native.mint_to_collection_v1_ix(
  tree_authority,
  recipient_pubkey,
  recipient_pubkey,  # delegate is same as owner initially
  merkle_tree_pubkey,
  payer_keypair,
  collection_authority_pubkey,
  collection_mint_pubkey,
  "",  # Empty to derive PDA
  "",  # Empty to derive PDA
  metadata,
  recent_blockhash
)

# Send transaction
{:ok, signature} = BubblegumNifs.SolanaRpc.send_transaction(client, tx)
```

### Transferring a Compressed NFT

Transferring requires merkle proof data from the Digital Asset Standard (DAS) API:

```elixir
# Get asset details and proof
{:ok, asset} = BubblegumNifs.SolanaRpc.get_asset_details(client, asset_id)
{:ok, proof} = BubblegumNifs.SolanaRpc.get_asset_proof(client, asset_id)

# Decode necessary hashes from base58
{:ok, root} = Base58.decode(proof["root"])
{:ok, data_hash} = Base58.decode(asset["data_hash"])
{:ok, creator_hash} = Base58.decode(asset["creator_hash"])

# Create transfer transaction
{:ok, tx} = BubblegumNifs.Native.transfer_ix(
  tree_authority,
  owner_pubkey,
  owner_pubkey,  # delegate is same as owner
  new_owner_pubkey,
  merkle_tree_pubkey,
  root,
  creator_hash,
  data_hash,
  asset["leaf_id"],  # nonce
  asset["leaf_id"],  # index
  proof["proof"],    # List of proof addresses
  recent_blockhash,
  owner_keypair
)

# Send transaction
{:ok, signature} = BubblegumNifs.SolanaRpc.send_transaction(client, tx)
```

## Solana RPC Functions

The library provides a convenient interface to Solana RPC endpoints:

```elixir
# Create a new client
client = BubblegumNifs.SolanaRpc.new("https://api.mainnet-beta.solana.com")

# Get recent blockhash
{:ok, blockhash} = BubblegumNifs.SolanaRpc.get_recent_blockhash(client)

# Get account information
{:ok, account_info} = BubblegumNifs.SolanaRpc.get_account_info(client, pubkey)

# Get compressed asset details (DAS API)
{:ok, asset} = BubblegumNifs.SolanaRpc.get_asset_details(client, asset_id)

# Get merkle proof for asset (DAS API)
{:ok, proof} = BubblegumNifs.SolanaRpc.get_asset_proof(client, asset_id)

# Calculate minimum rent for an account
{:ok, rent} = BubblegumNifs.SolanaRpc.get_mint_rent(client, account_size)
```

## Merkle Tree Size Calculation

The library provides a function to calculate the correct size for a merkle tree account:

```elixir
# Calculate size for a tree with default parameters
size = BubblegumNifs.MerkleTree.get_merkle_tree_size(14, 64)

# Calculate size with custom canopy depth
size = BubblegumNifs.MerkleTree.get_merkle_tree_size(14, 64, 8)
```

## Error Handling

The library uses a standardized error struct:

```elixir
%BubblegumNifs.Error{
  type: :rpc_error | :transaction_error | :instruction_error | :validation_error,
  message: String.t(),
  details: any()
}
```

Examples of error handling:

```elixir
case BubblegumNifs.SolanaRpc.send_transaction(client, tx) do
  {:ok, signature} ->
    IO.puts("Transaction succeeded with signature: #{signature}")

  {:error, %BubblegumNifs.Error{type: :rpc_error, message: msg, details: details}} ->
    IO.puts("RPC error: #{msg}")
    IO.inspect(details)

  {:error, %BubblegumNifs.Error{type: :transaction_error}} ->
    IO.puts("Transaction failed")
end
```

## Performance Considerations

1. **Account Size Calculation**: Correct account size calculation is critical for merkle trees. Undersized accounts will fail, while oversized accounts waste SOL.

2. **Proof Size**: When transferring assets, ensure the merkle proof is correctly retrieved from the DAS API.

3. **Large Tree Depth**: Trees with larger depths can support more NFTs but require more SOL to create.

## Best Practices

1. **Tree Parameters**: For most use cases, `max_depth=14` and `max_buffer_size=64` provide a good balance between cost and capacity.

2. **Public Trees**: Setting `public=true` allows anyone to mint to your tree.

3. **Error Handling**: Always handle errors, especially when interacting with RPC endpoints, as network issues can occur.

4. **URI Standards**: Use standard JSON metadata URIs (preferably on permanent storage like Arweave) for compatibility with marketplaces.

5. **Collection Verification**: When minting to collections, ensure that the collection authority is the correct signer.

## Limitations

1. **Compressed NFTs vs Traditional NFTs**: Compressed NFTs are stored differently from traditional NFTs and may have limited support in some wallets/marketplaces.

2. **RPC Availability**: Operations with compressed NFTs require RPC endpoints that support the Digital Asset Standard (DAS) API.

3. **Transaction Size**: Very deep merkle proofs can approach Solana's transaction size limits.

## Appendix: Common Tree Sizes

| max_depth | max_buffer_size | Approximate Capacity | Approximate Size |
|-----------|-----------------|----------------------|------------------|
| 14        | 64              | ~16k NFTs            | ~12KB            |
| 20        | 256             | ~1M NFTs             | ~48KB            |
| 24        | 1024            | ~16M NFTs            | ~192KB           |
| 30        | 2048            | ~1B NFTs             | ~512KB           |

## Contributing

Contributions to BubblegumNifs are welcome. Please ensure that your code follows the existing style and includes proper documentation.

## License

This library is licensed under the MIT License.