npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

drops-market

v0.5.5

Published

Agent-friendly CLI for DROPS — the NFT marketplace on XRPL EVM. Create collections, mint NFTs, upload art to Arweave, list, offer, and auction. --json output on every command.

Readme

drops-market

CLI for the DROPS NFT marketplace on XRPL EVM. Create, mint, upload, and trade NFTs from the command line.

Agent-friendly: all commands support --json for structured output.

Agents start here: drops.market/skill.md — the full playbook (every command, direct contract calls, the Arweave upload flow) in one fetchable file. drops.market/llms.txt is the short index.

Install

npm install -g drops-market

Setup

export WALLET_PRIVATE_KEY=0x...   # EVM private key (signs txns + upload auth)
drops status                       # Verify connection
drops balance                      # Verify the wallet has XRP

Generate a new wallet: drops wallet new

Fund the wallet first

A freshly generated wallet holds 0 XRP, and every write command needs gas. Deploying also costs the platform fee — 1 XRP plus 0.01 XRP per declared token, on both networks (the testnet factory charges the same fee in test XRP). Reads (status, info, list, tokens, browse) and uploads need no balance.

So a one-of-one costs 1.01 XRP, a 100-piece set 2 XRP, and a 10,000-item drop 101 XRP. drops status prints the live pricing and a sample curve.

| | Mainnet (default) | Testnet (--testnet) | |---|---|---| | To deploy a 1/1 | ~1.02 XRP — 1.01 fee + gas | same, in test XRP | | Where it comes from | An exchange or an existing wallet. There is no mainnet faucet — a human has to fund the address. | faucet.xrplevm.org — up to 90 XRP per claim (rate-limited, expect to retry) |

Arweave storage is not part of this: DROPS pays for it, so no XRP and no Turbo credits are needed to upload. See Image Uploads.

Quick Reference

Create a Collection

# With image upload (Mode B)
drops create collection --name "My Art" --symbol "ART" --price 1 --supply 100 --royalty 5 --image ./banner.png

# With a 16:9 banner and a 1:1 thumbnail (drops.market shows both)
drops create collection --name "My Art" --symbol "ART" --price 1 --supply 100 --image ./banner.png --thumbnail ./thumb.png

# With pre-uploaded URI (Mode A)
drops create collection --name "My Art" --symbol "ART" --price 1 --supply 100 --contract-uri "https://arweave.net/abc"

Create a Drop (batch, large supply)

drops create drop --name "Genesis" --symbol "GEN" --supply 1000 --price 0.5 --royalty 5 --images ./images/ --max-per-wallet 5

# Blind mint: art is uploaded up front, minters see a placeholder until you reveal
drops create drop --name "Genesis" --symbol "GEN" --supply 1000 --images ./images/ --blind --placeholder-image ./unrevealed.png
drops admin reveal 0xDrop --base-uri "<the Base URI the deploy printed>"

# Art isn't ready: deploy now, point pre-reveal tokens at your own metadata
drops create drop --name "Genesis" --symbol "GEN" --supply 1000 --hidden-uri "https://arweave.net/placeholder-json"

The first form uploads all images to Arweave, builds the metadata manifest, deploys the contract, and reveals — one command, and it aborts before deploying if any image fails to upload (a gapped manifest revealed on-chain is permanent).

Until a drop is revealed, tokenURI returns the same hiddenMetadataURI for every token. That is what blind means — one placeholder name and image for the whole supply — so identical metadata across different token IDs is expected, not a bug. On a non-blind --images deploy the CLI parks <baseURI>1.json in that field as a throwaway, because the same command reveals moments later and it is never shown; that is why hiddenMetadataURI in the output often ends in /1.json.

Create flags

| Flag | Applies to | What it does | |------|-----------|--------------| | --supply <n> | collection, drop | Required, minimum 1. Sets maxSupply and therefore the deploy fee (1 XRP + 0.01/token). Unlimited supply is not deployable. | | --image <file> | collection, drop | Uploads the collection banner (16:9) and builds the contract metadata around it. Skipped if --contract-uri is set. | | --thumbnail <file> | collection, drop | Uploads a square (1:1) thumbnail alongside the banner and adds it as thumbnail in the contract metadata — this is what grid cards on drops.market use. Only read when --image is also passed; on its own it does nothing. | | --contract-uri <uri> | collection, drop | Use metadata you already uploaded. Takes precedence over --image/--thumbnail. | | --images <dir> | drop | Uploads every image in the directory (token order = the first number in each filename), builds the manifest, and — unless --blind — reveals right after deploy. | | --names <file> | drop | Per-token names/descriptions: a JSON map keyed by filename, or a CSV of filename,name,description with a header row. Defaults to <Name> #<tokenId>. | | --blind | drop | Deploys unrevealed. Uploads --placeholder-image (or a bare placeholder JSON if omitted) as the pre-reveal metadata — the same URI for every token — and you call admin reveal later. | | --hidden-uri <uri> | drop | Sets the pre-reveal metadata URI directly, for a drop whose art isn't uploaded yet. Only read when neither --images nor --blind is passed — those two set the same on-chain field themselves. | | --max-per-wallet <n> | drop | Public-mint cap per address. 0 (default) means no limit. |

Mint

# Upload + mint in one step
drops mint 0xCollectionAddress --image ./art.png --name "Piece #1" --description "My first NFT"

# With traits — a {trait: value} object or a full [{trait_type, value}] array
drops mint 0xCollectionAddress --image ./art.png --name "Piece #1" --attributes '{"Rarity":"Rare"}'

# With pre-uploaded metadata URI, minted to someone else
drops mint 0xCollectionAddress --uri "https://arweave.net/xyz" --to 0xRecipient

# Batch mint on a drop
drops mint-drop 0xDropAddress --quantity 5

Minting into a collection you own uses creatorMint — free, and it works before you admin open-mint. Anyone else pays the mint price through the factory.

Upload (standalone)

drops upload image ./photo.png          # Returns Arweave URI
drops upload metadata ./meta.json       # Upload metadata JSON
drops upload batch ./images/            # Upload dir + build manifest (returns baseURI)

Browse & Read

drops info 0xAddress                    # Collection/drop details
drops list                              # All collections on the platform
drops list --creator 0xAddress          # Filter by creator
drops tokens 0xCollection               # List tokens in a collection
drops tokens 0xCollection --owner 0xYou # Only the ones held by an address
drops browse --limit 20 --offset 0      # Active marketplace listings

These need no wallet and no XRP — they are safe to call as often as you like.

Secondary Market

drops list-nft 0xCollection 1 10        # List token #1 for 10 XRP
drops buy 37                            # Buy listing #37
drops cancel-listing 37                 # Cancel your listing

Listing IDs are global, not per-collection. One counter runs across the entire marketplace, so the first listing on your collection is whatever number came next — it may well be #37. The ID tells you nothing about the collection or token; read those off the listing. drops list-nft --json returns the listingId it just created and drops browse shows which are still active. Don't guess one.

One listing per token. Creating a listing retires any previous listing for the same token, whoever made it — the old one flips inactive and buy on it reverts (ListingNotActive, or ListingStale for a listing that predates the rule). ERC-721 gives the marketplace no transfer hook, so a listing cannot notice the token leaving the seller: it goes dormant instead, and comes back to life at its original price if the token ever returns. Retiring it on the next listing breaks that cycle.

activeListingOf(collection, tokenId) on DropsMarket names the listing allowed to execute right now; buy and cancel clear it back to 0. The CLI surfaces both ends of this: list-nft reports the listing it replaced (supersededListingId in --json), and buy refuses a displaced listing before spending gas, failing LISTING_SUPERSEDED with supersededBy set to the ID you should buy instead.

The rule only fires when someone creates a new listing for that token, so cancel your listing before you move the token. Accepting an offer, settling an auction, or transferring to another of your own wallets leaves the old listing live: list at 10, take a 5 offer instead, buy it back at 15, and the 10 listing is still there for anyone to take.

Offers

drops offer 0xCollection 1 --amount 5 --duration 7d
drops accept-offer 1
drops cancel-offer 1

Auctions

drops auction create 0xCollection 1 --start-price 5 --reserve 20 --duration 3d
drops auction bid 1 --amount 25
drops auction settle 1
drops auction cancel 1

Creator Admin

drops admin open-mint 0xCollection      # Enable minting
drops admin close-mint 0xCollection     # Disable minting
drops admin set-price 0xCollection 2    # Update mint price to 2 XRP
drops admin reveal 0xDrop --base-uri "https://arweave.net/manifest/"
drops admin withdraw 0xCollection       # Withdraw earnings

admin reveal is not idempotent

DropsDrop.reveal() has no on-chain guard. It overwrites the drop's base URI on every call, so a second reveal silently repoints the art and metadata for every token in the drop — including ones already sold — and there is no undo. Nothing reverts and nothing warns you on-chain.

The CLI blocks it: revealing a drop that already reads revealed: true fails with ALREADY_REVEALED and sends no transaction. Add --force only if repointing the entire drop is genuinely the intent:

drops admin reveal 0xDrop --base-uri "https://arweave.net/manifest/" --force

Never retry a reveal blindly. If one times out, check drops info 0xDrop first — the transaction may have landed. And the base URI must end in /: the contract builds <baseURI><tokenId>.json, so a missing slash produces broken URIs for the whole drop rather than an error. The CLI prints the resulting pattern after a reveal so you can check it.

Networks — read this before your first deploy

Mainnet is the default — real XRP. DROPS is a mainnet product and the website only displays mainnet, so the CLI matches it. A drops create collection --supply 1 costs 1.01 XRP to deploy plus gas, and the result is live on drops.market immediately.

Pass --testnet to test for free. Testnet is headless: it is not indexed by drops.market, so nothing you create there shows up on the site.

| | Mainnet (default) | Testnet (--testnet) | |---|---|---| | Cost | Real XRP — 1 XRP + 0.01/token, plus gas | Free, but the same fee in test XRP — see Fund the wallet first | | On drops.market | Yes | No — headless | | Good for | Anything you want people to see | Testing contracts + scripts |

Explicit flags beat DROPS_CHAIN, so --testnet always wins over a stray shell export.

On testnet, verify from the terminal instead of the website:

drops info 0xCOLLECTION              # name, supply, price, owner, mint status
drops tokens 0xCOLLECTION            # minted token IDs
drops list --creator 0xYOUR_ADDRESS  # everything you've created

…plus the block explorer at https://explorer.testnet.xrplevm.org/address/0xCOLLECTION. Your Arweave image/metadata URLs work in any browser on either network.

Flags

Global — valid on any command. Both --flag value and --flag=value are accepted.

| Flag | Description | |------|-------------| | --mainnet | Use XRPL EVM mainnet (default — what drops.market displays) | | --testnet | Use XRPL EVM testnet (free, headless — not shown on drops.market) | | --json | JSON output for agents/scripts | | --dry-run | create collection / create drop only. Validates and reports what the command would do — including the exact deploy fee for your --supply — without sending a transaction or uploading. Use it before any mainnet write: a deploy costs real XRP and the contract is immutable. Any other command rejects it with an acceptedBy list. | | --version | Print the installed CLI version and exit | | --help | Usage for the command in front of it (drops create drop --help); on its own, the full command list |

--dry-run and per-command --help are recent. Older builds ignore flags they don't recognize instead of erroring — so on a stale install, --dry-run does not stop anything and drops mint-drop 0xDrop --help mints for real. Check drops --version before you trust either one on mainnet; if that flag prints nothing, the build predates both.

Per-command flags are listed by drops <command> --help and, for deploys, in Create flags above.

JSON output

--json wraps results in one envelope, so an agent can branch on a single field:

// success
{ "ok": true, "version": "0.5.4", "chain": "mainnet", "chainId": 1440000, /* command fields */ }

// failure — on stdout, exit code 1
{ "ok": false, "version": "0.5.4", "chain": "mainnet",
  "error": { "code": "NO_PRIVATE_KEY", "message": "WALLET_PRIVATE_KEY is not set", "hint": "…" } }

Failures are written to stdout rather than stderr on purpose: the common agent pattern is JSON.parse(execSync('drops … --json')), which never sees stderr.

Always check the exit code, not just the payload. A few argument-validation errors still exit non-zero with a plain-text message and print nothing to stdout, so a parser that trusts stdout alone sees an empty string and throws something unrelated to the real problem. Non-zero exit means it did not happen, whatever came back on stdout.

Environment Variables

| Variable | Description | |----------|-------------| | WALLET_PRIVATE_KEY | EVM private key (required for write commands, also signs upload auth). PRIVATE_KEY works as a fallback. | | DROPS_CHAIN | testnet or mainnet (default mainnet). An explicit --testnet/--mainnet always wins over this. | | DROPS_API_URL | Override the upload/index API (default https://api.drops.market) | | NO_COLOR | Suppress ANSI colors. Already suppressed automatically when stdout is not a terminal. |

Contracts

Mainnet (default)

| Contract | Address | |----------|---------| | DropsFactory | 0x00844CaE1ecfd0b73356f6d9cB7A7e421EC9c7A8 | | DropsMarket | 0xb5D697C249f219E9D47969bCcEbE7E6A65291ccA | | DropsOffers | 0x917AA02C718EFbF0621FD72Dd39E70C074b6732d | | DropsAuction | 0x4c73c939737Eaa3dAC015528cF967D52C9B344b7 | | DropsLens | 0x46E0bf48a18f44Dc25B74EdDA2795f7Baa6934af |

Testnet (--testnet)

| Contract | Address | |----------|---------| | DropsFactory | 0x46E0bf48a18f44Dc25B74EdDA2795f7Baa6934af | | DropsMarket | 0x1689e3D5CfF83C865934bF9504940b08ca358dBF | | DropsOffers | 0x9DB977b941fDDdCC3Ab194e3cC83F2cDAc562C26 | | DropsAuction | 0x72BC803a23BBBB2f5B1e823a9b57F6F4a430dFAA | | DropsLens | 0x7895DefEf770B588b46329E62E7d763Ff175dF5a |

The mainnet Lens and the testnet Factory share an address — same deployer, same nonce on both chains, so the two chains produced identical addresses for different contracts. It is not a copy-paste bug; do not "fix" it. Re-verify in one call each if you doubt it:

cast call 0x46E0bf48a18f44Dc25B74EdDA2795f7Baa6934af "getCollectionStats()" --rpc-url https://rpc.xrplevm.org
cast call 0x46E0bf48a18f44Dc25B74EdDA2795f7Baa6934af "totalCollections()(uint256)" --rpc-url https://rpc.testnet.xrplevm.org

Fee Structure

  • Deploy fee: 1 XRP + 0.01 XRP per declared token
  • Primary sale: 2.5% platform fee on mints
  • Secondary sale: 1% platform fee + creator royalties (0-10%)

| Supply | Deploy cost | |---|---| | 1 (a one-of-one) | 1.01 XRP | | 100 | 2 XRP | | 1,000 | 11 XRP | | 10,000 | 101 XRP |

Deploy cost scales with supply because the platform's own cost does — every declared token is permanent Arweave storage DROPS funds. Read the live price for any supply with deployFeeFor(maxSupply) on the factory, or drops status.

Unlimited supply is not deployable. --supply is required on both create collection and create drop, and must be at least 1 — an uncapped set has no way to price. Collections deployed unlimited before this change still work.

These fees are the same on both networks — the testnet factory charges an identical fee, in test XRP. Testnet makes the XRP free, not the fees, so a testnet wallet still needs a funded balance before it can deploy.

Image Uploads (Arweave)

Uploads are platform-funded — DROPS pays for permanent Arweave storage, so it's free to you. The CLI sends your image to api.drops.market, which compresses it and uploads it; each request is authenticated with a short-lived signature from your WALLET_PRIVATE_KEY (no gas, no separate Arweave wallet, no Turbo credits). You need XRP only for gas and the deploy/mint fees.

Limits worth knowing before a big drop:

  • 10 MB per file. The CLI checks the size locally and refuses before spending bandwidth.
  • 50 images (or ~40 MB) per request, whichever comes first. upload batch and create drop --images chunk large directories for you; a chunk that reports any failure aborts the whole command rather than deploying a drop with holes in its metadata.
  • Everything is re-encoded to WebP (quality 85, max 1024px on the long edge) server-side, so uploading a 4000px PNG just wastes upload time — it is stored compressed either way.
  • Fair-use quota: 500 MB per wallet, plus 500 MB for each collection you've deployed (ample for a full 10K-item drop). There is also a platform-wide daily ceiling. Either one returns HTTP 429; the error says which.

Set DROPS_API_URL to point at a different backend (default https://api.drops.market). If uploads start failing, curl https://api.drops.market/health reports API, indexer, and Arweave credit status — a low credit status there means the platform wallet needs a top-up, not you.

You can also skip uploads entirely by providing --uri with a pre-uploaded Arweave (or any HTTPS) URL — this keeps your own Arweave attribution.

License

MIT