The needle is warming up
The needle is warming up

Technical Insert · Volume I
How a tattoo gets onto Solana: Bubblegum V2 + Token-2022 + a very stubborn Merkle tree.
01 / The Flow
Every mint passes through the same sequence — the only thing that differs between walk-ins and enterprise issuers is which Merkle tree receives the leaf.
User connects a Solana wallet and picks a flash from the sheet. Selection is stored locally until signature.
POST /api/mint/prepare builds a Versioned Transaction with Bubblegum V2 mint, Token-2022 non-transferable flag, and partial sig from mint authority.
User signs. No blind approvals — credential, recipient, and tree are in plain view. Signed tx sent to POST /api/mint/confirm.
Server submits via Helius RPC. On confirmation, cNFT leaf lands in the shared Merkle tree, DB stores the metadata pointer, Redis cache invalidates gallery.
Credential is now soulbound. Transferability is blocked at the mint level. Revocation requires tree authority (enterprise issuers only).
Mint Flow Diagram
02 / The Bill
Standard SPL NFT
$2.00
per mint, excluding IPFS
~ 0.012 SOL
Metaplex cNFT (INKD)
$0.0005
per mint, Bubblegum V2
~ 0.0000028 SOL
10k Issuance Cost
$5.00
fits on a petty cash slip
~ 0.028 SOL
| Metric | SPL NFT | INKD cNFT | Delta |
|---|---|---|---|
| Mint cost | $2.00 | $0.0005 | 4000x cheaper |
| Transfer? | Yes | Blocked | Soulbound |
| Storage | Rent-exempt SOL | Merkle leaf | ~0 bytes |
| 10k batch | $20,000 | $5.00 | 99.97% save |
| Read latency | 100–300ms | <200ms DAS | same |
03 / The API
Hono on Node on Railway. JSON in, JSON out, Zod validated, Redis ratelimited. Nothing fancy — fancy loses in production.
/api/mint/prepareBuild a signed mint tx↓curl -X POST https://api.inkd.fun/api/mint/prepare \
-H "content-type: application/json" \
-d '{"wallet": "...", "credential": "first-time"}'/api/mint/confirmSubmit signed tx + persist record↓curl -X POST https://api.inkd.fun/api/mint/confirm \
-H "content-type: application/json" \
-d '{"serializedTx": "base64..."}'/api/galleryLatest 100 cNFTs (Redis cached)↓curl https://api.inkd.fun/api/gallery/api/gallery/:walletPer-wallet ink history↓curl https://api.inkd.fun/api/gallery/WaLL3TPuBKey.../api/verifyMerkle-proof ownership check↓curl -X POST https://api.inkd.fun/api/verify \
-H "content-type: application/json" \
-d '{"cnft": "INK...", "owner": "..."}'/api/issuer/register2nd hook: onboard an issuer↓curl -X POST https://api.inkd.fun/api/issuer/register \
-H "authorization: Bearer $INKD_KEY"/api/issuer/batch-mint2nd hook: bulk attest↓curl -X POST https://api.inkd.fun/api/issuer/batch-mint \
-H "authorization: Bearer $INKD_KEY"OpenAPI spec lives at /openapi.json (after backend deploy).
Tree
max_depth 14, max_buffer_size 64. 16,384 leaves per tree. New trees auto-provision once full.
Token
Non-transferable extension. Frozen delegate. Revocable by tree authority when required.
Metadata
nft.storage for artwork + schema. Helius DAS for read path. Everything mirrored in Postgres for fast UI reads.