| name | nft-drop |
| description | Use when the user wants to deploy an ERC-721A NFT collection, pin images + metadata to IPFS, batch-mint, or otherwise ship an NFT drop from this repo. Triggers on phrases like "deploy NFT", "mint NFTs", "launch a collection", "drop NFTs", "upload to IPFS", "NFT collection on Ethereum/Sepolia", or any request that involves the `nft-mint` CLI. |
nft-drop — shipping an ERC-721A collection
You are helping the user ship an NFT collection using this repo's nft-mint CLI (ERC-721A contract + Pinata IPFS + Foundry). The CLI is designed for one-command drops — lean into that instead of composing low-level primitives.
The one command that does everything
node packages/cli/dist/index.js drop \
--manifest <path-to-manifest.json> \
--network sepolia \
--mint \
--no-prompt
drop runs upload → deploy → batch-mint in sequence. All the primitives (upload, deploy, batch-mint) also exist as standalone commands for advanced flows, but prefer drop unless the user explicitly needs to split the steps.
When the user asks to deploy / mint an NFT collection
Follow this order — don't skip ahead:
1. Preflight (always do this first)
Run these checks in parallel via Bash:
test -f .env && echo ok — user must have a .env file; if not, tell them to cp .env.example .env and fill it in.
test -f packages/cli/dist/index.js && echo ok — CLI must be built; if missing, run pnpm --filter @nft-minter/cli build.
command -v forge >/dev/null && forge --version — Foundry must be installed; if missing, point them to https://book.getfoundry.sh/getting-started/installation.
Check that .env has non-placeholder values for: PRIVATE_KEY, RPC_URL_SEPOLIA (or _MAINNET), PINATA_JWT, ETHERSCAN_API_KEY. Do not print their values — just confirm presence with something like grep -cE "^PINATA_JWT=.{20,}$" .env.
If any are missing, stop and tell the user exactly which key is missing and where to get it:
2. Scaffold if they don't have a manifest yet
Ask these questions (batch them — don't ping-pong):
- Collection name?
- How many tokens?
- Target network (sepolia default, mainnet only if they're sure)?
- Mint price in ETH (0 default — owner-only mint is free anyway)?
Then:
node packages/cli/dist/index.js init <slug> --name "<name>" --count <n>
This creates <slug>/manifest.json + <slug>/images/ with a README explaining what to drop in there.
3. Get the images in place
Tell the user to copy their images into <slug>/images/ named 1.png, 2.png, … <n>.png (matching the file field in the manifest). Any image format works (png/jpg/gif/webp/svg).
If the user has oddly-named images or custom metadata (traits, descriptions per token), edit <slug>/manifest.json directly — the file field can point to any relative path. Keep ManifestSchema valid (see packages/shared/src/metadata.ts).
Verify image count matches manifest:
jq '.tokens | length' <slug>/manifest.json
ls <slug>/images/ | grep -cEv 'README'
4. Ship it
Default one-shot:
node packages/cli/dist/index.js drop \
--manifest <slug>/manifest.json \
--network <network> \
--price <eth> \
--mint
Drop --no-prompt for CI or when the user says "don't ask me". Drop --mint if they only want to deploy the contract without minting tokens yet.
5. Report what happened
After drop succeeds, pull these out of its output and show the user:
- Contract address
- Etherscan link (auto-printed)
- NFT viewer link — OpenSea for mainnet, Etherscan's
/nft/ page for testnets (OpenSea discontinued testnet support in 2025)
- Images folder CID + Metadata folder CID
Suggest the next step based on their goal:
- "View the NFT" — on mainnet, OpenSea; on Sepolia / other testnets, the printed Etherscan NFT URL. Metadata indexing can lag a few minutes on either.
- "Mint more to another wallet" —
nft-mint mint --contract <addr> --to <addr> --quantity <n> --network <net>
- "Set a mint price later" — call
setMintPrice(priceWei) via cast or a follow-up script
- "Make it public mintable" — already is,
mint() is payable and open to anyone; owner keeps ownerMint (free)
Gotchas to watch for
If something breaks
- Upload fails with 401/403 —
PINATA_JWT wrong or missing Files: Write permission.
- Upload fails with 413 — image too big. Pinata free tier caps individual files around 25 MB. Compress or split.
- Deploy fails with "insufficient funds" — wallet from
PRIVATE_KEY needs Sepolia ETH. Use https://sepoliafaucet.com or Alchemy's faucet.
- Deploy succeeds but verification fails — non-fatal; re-run
nft-mint verify separately, or pass --no-verify and skip Etherscan.
tokenURI(1) returns empty — baseURI wasn't set at deploy time. Check .nft-mint.json; if baseUri is empty, run upload first or call setBaseURI(string) via cast send <addr> "setBaseURI(string)" "ipfs://<cid>/".
- NFT viewer shows metadata but no image — IPFS gateway lag. Check
ipfs://<imagesCid>/1.png via https://gateway.pinata.cloud/ipfs/ — if it loads, the viewer will catch up within ~10 min.
- Testnet OpenSea links don't work. OpenSea discontinued testnet support in 2025. Use the Etherscan
/nft/<contract>/<tokenId> page on Sepolia — it has a built-in NFT viewer.
What NOT to do
- Don't tell the user to open the Pinata dashboard and upload manually. The old README used to say this — the CLI now handles folder pinning automatically via the REST API.
- Don't compose
upload + deploy + batch-mint as three separate commands unless they explicitly want that. drop exists specifically to collapse that.
- Don't use
forge create directly. The repo has script/Deploy.s.sol and the CLI's deploy wires it up with the right env vars.
- Don't regenerate the manifest from scratch if they already have one. Edit the existing file.
- Don't commit
.env or anything with a real PRIVATE_KEY.
Quick reference
| Goal | Command |
|---|
| Fresh start | nft-mint init <slug> --name "<name>" --count <n> |
| Upload + deploy + mint | nft-mint drop --manifest <slug>/manifest.json --network sepolia --mint |
| Just pin to IPFS | nft-mint upload --manifest <slug>/manifest.json |
| Just deploy (uses saved baseURI) | nft-mint deploy --manifest <slug>/manifest.json --network sepolia |
| Mint all to a wallet | nft-mint batch-mint --contract <addr> --manifest <slug>/manifest.json --network sepolia --owner |
| Mint one to someone | nft-mint mint --contract <addr> --to <addr> --quantity 1 --network sepolia |
| Verify on Etherscan | nft-mint verify --address <addr> --network sepolia --name … --symbol … --base-uri … --max-supply … --price … --owner … |
The CLI binary is at packages/cli/dist/index.js when run from the repo root. A global nft-mint works if the user has run pnpm link or npm i -g — otherwise prefix with node packages/cli/dist/index.js.