# Core Candy Machine CLI Reference

Candy Machine enables NFT drops with configurable minting rules (guards).

## Directory Structure

```
my-candy-machine/
├── assets/
│   ├── 0.png                 # Image for NFT #0
│   ├── 0.json                # Metadata for NFT #0
│   ├── 1.png
│   ├── 1.json
│   ├── 2.png
│   ├── 2.json
│   ├── ...
│   ├── collection.png        # Collection image (required)
│   └── collection.json       # Collection metadata (must have "name" field)
├── asset-cache.json          # Generated after upload
└── cm-config.json            # Generated by wizard or created manually
```

**Asset naming**: Files must be numbered starting from 0 (0.png, 0.json, 1.png, 1.json, etc.)

**collection.json example**:
```json
{
  "name": "My Collection",
  "description": "A collection of NFTs",
  "image": "collection.png"
}
```

---

## Workflow Option 1: Wizard (Recommended)

```bash
mplx cm create --wizard
```

The wizard handles everything:
1. Validates assets and configuration
2. Uploads all assets with progress tracking
3. Creates the candy machine on-chain
4. Inserts all items with transaction progress
5. Provides completion summary with links

---

## Workflow Option 2: Manual

```bash
# 1. Create template directory structure
mplx cm create --template

# 2. Add your assets to the assets/ folder
#    - 0.png, 0.json, 1.png, 1.json, etc.
#    - collection.png, collection.json
# 3. Create a Core collection for the candy machine
mplx core collection create

# 4. Edit cm-config.json: add the collection address, configure guards, etc.
#    The "collection" field MUST contain a valid Core collection address.

# 5. Upload assets to storage
mplx cm upload

# 6. Create candy machine (reads cm-config.json)
mplx cm create

# 7. Insert items into candy machine
mplx cm insert

# 8. Validate (optional but recommended)
mplx cm validate --onchain
```

> **Note:** You MUST create a Core collection first (e.g. with `mplx core collection create`) and add its address to the `"collection"` field in `cm-config.json` before running `mplx cm create`. The CLI does NOT create the collection automatically — it will error if no collection address is provided.

---

## Commands Reference

```bash
mplx cm create --wizard              # Interactive wizard (does everything)
mplx cm create --template            # Create template directory
mplx cm create <DIR>                 # Create from existing cm-config.json
mplx cm upload <DIR>                 # Upload assets to storage
mplx cm insert <DIR>                 # Insert items into candy machine
mplx cm validate <DIR>               # Validate uploads
mplx cm validate <DIR> --onchain     # Validate items inserted on-chain
mplx cm fetch <ADDR>                 # Fetch candy machine info
mplx cm fetch <ADDR> --items         # Fetch with all items
mplx cm withdraw                     # Withdraw from CM directory (reads cm-config.json)
mplx cm withdraw <DIR>               # Withdraw and delete (recovers rent)
mplx cm withdraw --address <ADDR>    # Withdraw by address
```

---

## Guards Configuration

Guards control who can mint, when, and at what cost.

### Available Guards

| Category | Guards |
|----------|--------|
| **Payment** | `solPayment`, `solFixedFee`, `tokenPayment`, `token2022Payment`, `nftPayment`, `assetPayment`, `assetPaymentMulti` |
| **Access Control** | `addressGate`, `allowList`, `nftGate`, `tokenGate`, `assetGate`, `programGate`, `thirdPartySigner` |
| **Time-Based** | `startDate`, `endDate` |
| **Limits** | `mintLimit`, `allocation`, `nftMintLimit`, `assetMintLimit`, `redeemedAmount` |
| **Burn Guards** | `nftBurn`, `tokenBurn`, `assetBurn`, `assetBurnMulti` |
| **Special** | `botTax`, `edition`, `vanityMint` |
| **Freeze** | `freezeSolPayment`, `freezeTokenPayment` |

### cm-config.json Example

The config uses a nested `name/directory/config` wrapper format:

```json
{
  "name": "my-candy-machine",
  "directory": "/path/to/my-candy-machine",
  "config": {
    "collection": "<COLLECTION_ADDRESS>",
    "itemsAvailable": 10,
    "isMutable": true,
    "isSequential": false,
    "guardConfig": {
      "solPayment": {
        "lamports": 1000000000,
        "destination": "<PAYMENT_WALLET>"
      },
      "startDate": {
        "date": "2024-01-01T00:00:00Z"
      },
      "mintLimit": {
        "id": 1,
        "limit": 1
      },
      "botTax": {
        "lamports": 10000000,
        "lastInstruction": true
      }
    },
    "groups": []
  }
}
```

Key fields: `itemsAvailable` (not `number`), `collection` (must be set before `mplx cm create`). `sellerFeeBasisPoints` is set at the collection level (when creating the Core collection), not in the candy machine config.

### Guard Groups (Multiple Minting Phases)

```json
{
  "name": "my-candy-machine",
  "directory": "/path/to/my-candy-machine",
  "config": {
    "collection": "<COLLECTION_ADDRESS>",
    "itemsAvailable": 100,
    "isMutable": true,
    "isSequential": false,
    "guardConfig": {},
    "groups": [
      {
        "label": "wl",
        "guards": {
          "allowList": {
            "merkleRoot": "<MERKLE_ROOT_HASH>"
          },
          "solPayment": {
            "lamports": 500000000,
            "destination": "<PAYMENT_WALLET>"
          },
          "startDate": {
            "date": "2024-01-01T00:00:00Z"
          }
        }
      },
      {
        "label": "public",
        "guards": {
          "solPayment": {
            "lamports": 1000000000,
            "destination": "<PAYMENT_WALLET>"
          },
          "startDate": {
            "date": "2024-01-02T00:00:00Z"
          }
        }
      }
    ]
  }
}
```

---

## Common Guard Configurations

### SOL Payment

```json
"solPayment": {
  "lamports": 1000000000,
  "destination": "<WALLET_ADDRESS>"
}
```
Note: 1 SOL = 1,000,000,000 lamports

### Start/End Date

```json
"startDate": {
  "date": "2024-01-01T00:00:00Z"
},
"endDate": {
  "date": "2024-01-31T23:59:59Z"
}
```

### Mint Limit (per wallet)

```json
"mintLimit": {
  "id": 1,
  "limit": 3
}
```

### Allow List (whitelist)

```json
"allowList": {
  "merkleRoot": "<MERKLE_ROOT_HASH>"
}
```

To generate the Merkle root from a list of wallet addresses, use the Umi SDK:

```typescript
import { getMerkleRoot } from '@metaplex-foundation/mpl-core-candy-machine';

const allowedWallets = [
  'Addr1...', 'Addr2...', 'Addr3...',
];
const merkleRoot = getMerkleRoot(allowedWallets);
// Returns a Uint8Array — convert to hex string for cm-config.json:
const merkleRootHex = Buffer.from(merkleRoot).toString('hex');
```

When minting with an allowlist, the minter must provide a Merkle proof (see Minting section below).

### Bot Tax

```json
"botTax": {
  "lamports": 10000000,
  "lastInstruction": true
}
```
Charges failed mints to deter bots.

---

## Withdrawing

After all items are minted (or to cancel):

```bash
# From directory with cm-config.json
mplx cm withdraw

# Or by address
mplx cm withdraw --address <CANDY_MACHINE_ADDRESS>

# Force without confirmation
mplx cm withdraw --force
```

⚠️ **WARNING**: Withdrawing permanently deletes the candy machine. Non-redeemed items are lost.

---

## Minting from a Candy Machine

The CLI **creates and configures** candy machines but does not have a mint command. Users mint via:

1. **Frontend app** — Build a minting UI using the Umi SDK (`@metaplex-foundation/mpl-core-candy-machine`)
2. **Script** — Mint programmatically with Umi:

```bash
npm install @metaplex-foundation/mpl-core-candy-machine @metaplex-foundation/umi-bundle-defaults
```

> **Umi setup required** — see `./sdk-umi.md` "Basic Setup" for full Umi initialization (createUmi, keypairIdentity, etc.)

```typescript
import { mintV1 } from '@metaplex-foundation/mpl-core-candy-machine';
import { some } from '@metaplex-foundation/umi';

import { generateSigner } from '@metaplex-foundation/umi';

// Simple mint (no guard groups)
const asset = generateSigner(umi);
await mintV1(umi, {
  candyMachine: candyMachineAddress,
  collection: collectionAddress,
  asset,
}).sendAndConfirm(umi);

// With guard groups — must specify group label + mintArgs for active guards
const asset2 = generateSigner(umi);
await mintV1(umi, {
  candyMachine: candyMachineAddress,
  collection: collectionAddress,
  asset: asset2,
  group: some('public'),
  mintArgs: {
    solPayment: some({ destination: paymentWallet }),
  },
}).sendAndConfirm(umi);
```

For allowlist minting, pass the Merkle proof in `mintArgs`:

```typescript
import { getMerkleProof } from '@metaplex-foundation/mpl-core-candy-machine';

const proof = getMerkleProof(allowedWallets, minterAddress);

const asset3 = generateSigner(umi);
await mintV1(umi, {
  candyMachine: candyMachineAddress,
  collection: collectionAddress,
  asset: asset3,
  group: some('wl'),
  mintArgs: {
    allowList: some({ merkleProof: proof }),
    solPayment: some({ destination: paymentWallet }),
  },
}).sendAndConfirm(umi);
```

For full guard-specific minting details, see the [Candy Machine documentation](https://developers.metaplex.com/core-candy-machine/guards).

---

## Localnet Limitations

The `mplx cm upload` step requires Irys storage and **will not work on localnet**. For localnet testing, you can skip the upload step by manually creating an `asset-cache.json` file with pre-populated URIs:

```json
{
  "assetItems": {
    "0": {
      "name": "My NFT #0",
      "image": "0.png",
      "json": "0.json",
      "loaded": false,
      "onChain": false,
      "imageType": "image/png",
      "imageUri": "https://example.com/0.png",
      "jsonUri": "https://example.com/0.json"
    }
  }
}
```

With this cache in place, you can proceed directly to `mplx cm create` and `mplx cm insert`.

---

## Best Practices

1. **Test on devnet first** before mainnet deployment
2. **Validate assets** before uploading: consistent naming (0.png, 0.json, etc.)
3. **Keep file sizes reasonable** (< 10MB per image recommended)
4. **Include collection.json** with a valid "name" field
5. **Add at least one guard** (otherwise anyone can mint for free)
6. **Save explorer links** for verification
7. **Back up cm-config.json** after creation
