CLI toolset for mass-uploading images to Swarm (decentralized storage), generating ERC-1155 metadata, minting NFTs on the OpenRun contract, and syncing NFT data to MySQL.
- Node.js (v18+)
- npm
- A running Bee node (light or full mode) with a usable postage stamp batch
- A Swarm gateway URL for public reads (your own node-backed gateway or the public
https://api.gateway.ethswarm.org) - Access to an OpenRun ERC-1155 contract (Base Sepolia)
- MySQL database (for
sync-dbstep)
npm install
cp .env.example .env
# Edit .env with your credentials- Every image is uploaded individually and gets its own permanent, content-addressed Swarm reference (64-char hex). Nested category subfolders are flattened — each file inside becomes its own item.
- References never change: adding new images later doesn't disturb existing ones, and re-uploading identical content is free (chunks are deduplicated by content address).
- Uploads are pinned on the Bee node (
pin: true) so its garbage collection never evicts content your gateway serves, and pushed to the network synchronously (deferred: false). - Public URLs are built as
${SWARM_GATEWAY_URL}/bzz/<ref>— used in each metadataimagefield and the contract baseURI. - Image basenames must be unique per parent folder (
avatar/,thumbnail/): token IDs arekeccak256("<parent>/<name>"), soupload-imagesfail-fast aborts if two files (NFC-normalized) would collide.
images/
rarity.json - optional rarity mapping (keys file basenames or subfolder names)
avatar/ - parent folder (one token per image)
bodyAcc/ - category: flat image files
top/ - category: may contain nested subfolders
top1/ - ...whose images are flattened into the category
thumbnail/
...
Each command can be run individually, allowing you to verify results between steps. All intermediate files are saved to ./output/.
npm run upload-images- Reads: Image files from
IMAGES_DIR(default./images) - Produces:
./output/grouped-upload-results.json— one item per image with its Swarmrefand gatewayurl - Verify: Open a few
urlvalues from the JSON in a browser. Pin status can be checked withcurl $SWARM_BEE_API_URL/pins/<ref>(200 = pinned).
npm run generate-metadata- Reads:
./output/grouped-upload-results.json(andimages/rarity.jsonif present) - Produces:
./output/metadata/*.jsonand./output/metadata-results.json - Verify: Open the JSON files in
./output/metadata/to confirm each token has correctname,description, and animageURL pointing to your Swarm gateway.
npm run upload-metadata-
Reads:
./output/metadata/folder (uploaded as a single Swarm collection) -
Produces:
./output/metadata-ref.txt -
Verify: Fetch one token through the gateway — note that the bare collection root (
/bzz/<ref>/) returns 404 because the manifest has no index document; always append a file path:TOKEN=$(ls output/metadata | head -1 | sed 's/\.json$//') curl "$SWARM_GATEWAY_URL/bzz/$(cat output/metadata-ref.txt)/$TOKEN.json"
npm run set-base-uri- Reads:
./output/metadata-ref.txt(or pass--ref <ref>) - Produces: On-chain transaction setting
baseURIon the contract - Verify: Confirm the transaction on a block explorer (Base Sepolia). The base URI should be
${SWARM_GATEWAY_URL}/bzz/<ref>/with a trailing slash — the contract appends<tokenId>.json.
npm run mint-tokens -- --to <wallet-address>- Reads:
./output/metadata-results.json, CLI flags (--to, optional--amount) - Produces: On-chain mint transaction(s)
- Note: Token IDs are keccak256 hashes of
"<parent>/<image basename>"(not sequential integers) - Verify: Check the recipient's token balance on the block explorer.
npm run sync-db- Reads:
./output/metadata-results.json - Produces: Records in the
tb_nftsMySQL table (table is rebuilt on each sync) - Note: Merges avatar and thumbnail entries by name into a single row with bare Swarm references in
thumbnail_ref/avatar_ref/avatar2_ref(avatar2_refholds the 뒤_-prefixed back image of hair items). Orphans (avatar-only or thumbnail-only) are allowed with nullable columns. Consumers build public URLs as${SWARM_GATEWAY_URL}/bzz/<ref>. - Verify: Query the database to confirm records were inserted correctly.
To run all steps at once with confirmation prompts between steps:
npm run pipeline -- --to <wallet-address> --dir ./imagesThe collection is designed to grow:
- Drop the new image files into
images/(unique basenames per parent folder!) and runupload-images— existing images re-resolve to their old references for free; only new content is uploaded. - Run
generate-metadata+upload-metadata— the full metadata folder is regenerated and re-uploaded, producing a new metadata reference. - Run
set-base-urito re-point the contract at the new metadata reference (old tokens keep resolving — their<tokenId>.jsonfiles are included in the new folder). - Mint the new tokens and
sync-db.
See .env.example for all required variables:
| Variable | Description |
|---|---|
SWARM_BEE_API_URL |
Bee node HTTP API (e.g. http://localhost:1633) — used for uploads |
SWARM_STAMP_BATCH_ID |
Postage stamp batch ID used to stamp uploads |
SWARM_GATEWAY_URL |
Swarm API Gateway you would like the public to use to read your uploaded data. Default: public gateway |
RPC_URL |
Blockchain RPC endpoint |
PRIVATE_KEY |
Wallet private key for contract transactions |
CONTRACT_ADDRESS |
Deployed OpenRun contract address |
DB_HOST |
MySQL host |
DB_PORT |
MySQL port |
DB_USER |
MySQL user |
DB_PASSWORD |
MySQL password |
DB_NAME |
MySQL database name |
METADATA_BASE_NAME |
Default NFT name prefix |
METADATA_BASE_DESCRIPTION |
Default NFT description |
IMAGES_DIR |
Source images directory |
src/
config/index.ts - Environment configuration
types/index.ts - TypeScript interfaces
services/
swarmService.ts - Swarm upload via bee-js (per-image + metadata collection)
metadataService.ts - ERC-1155 metadata JSON generation
contractService.ts - OpenRun contract interaction (ethers.js)
dbService.ts - MySQL tb_nfts CRUD operations
utils/
swarmUrl.ts - Build gateway URLs (bzz) for image refs and baseURI
mergeRecords.ts - Merge avatar/thumbnail metadata into NFT records
rarityIndex.ts - Resolve rarity from images/rarity.json
confirm.ts - Interactive yes/no prompt for pipeline steps
commands/
uploadImages.ts - CLI: upload images
generateMetadata.ts - CLI: generate metadata JSONs
uploadMetadata.ts - CLI: upload metadata folder to Swarm
setBaseUri.ts - CLI: set contract baseURI
mintTokens.ts - CLI: mint tokens
syncDb.ts - CLI: sync to database
pipeline.ts - CLI: full end-to-end pipeline
index.ts - Main CLI entry point
- Contract repo:
openrun-contract(Foundry, Solidity) - Contract: OpenRun ERC-1155 on Base Sepolia