# Cardano Token Registry

## Complete Guide: Registering a Native Token in the Cardano Token Registry

> Last updated: February 2026

***

### Table of Contents

1. Overview
2. Official Documentation Links
3. Prerequisites
4. Phase 1 - Environment Setup (WSL + Ubuntu)
5. Phase 2 - Install token-metadata-creator
6. Phase 3 - Prepare Your Policy Keys
7. Phase 4 - Prepare Your Logo
8. Phase 5 - Create the Metadata Entry
9. Phase 6 - Sign and Finalize
10. Phase 7 - Verify the JSON
11. Phase 8 - Submit the Pull Request
12. Registry Field Reference
13. Common Mistakes & How to Avoid Them
14. Best Practices Checklist

***

### Overview

The Cardano Token Registry is an off-chain metadata repository maintained by the Cardano Foundation. It maps native token subjects (policy ID + asset name hex) to human-readable metadata like names, tickers, descriptions, and logos.

**Key facts:**

* Entirely off-chain — no ADA or blockchain transaction required
* Metadata is submitted via GitHub Pull Request
* Signatures prove policy ownership cryptographically
* The process works on Windows via WSL (Windows Subsystem for Linux)

***

### Official Documentation Links

| Resource                        | URL                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Cardano Token Registry (GitHub) | <https://github.com/cardano-foundation/cardano-token-registry>                                               |
| token-metadata-creator tool     | <https://github.com/input-output-hk/offchain-metadata-tools>                                                 |
| Tool manual                     | <https://input-output-hk.github.io/offchain-metadata-tools/>                                                 |
| Registry submission guide       | <https://github.com/cardano-foundation/cardano-token-registry/wiki/How-to-prepare-an-entry-for-the-registry> |
| CIP-26 (off-chain metadata)     | <https://github.com/cardano-foundation/CIPs/tree/master/CIP-0026>                                            |

***

### Prerequisites

Before starting, you need:

* **Policy ID** of your native token
* **Policy signing key** (`.skey` file, used during minting)
* **Policy script** file (`.script` or `.json`)
* **Asset name** in hex (e.g. `fSLVR` → `66534c5652`)
* **Logo** image (see Phase 4 for format requirements)
* **GitHub account**
* Windows PC with WSL, or a Linux machine

> 💡 **Finding your asset name in hex:** You can convert ASCII to hex online at <https://string-functions.com/string-hex.aspx> or in Linux: `echo -n "YOURTOKEN" | xxd -p`

> 💡 **Your subject** is always: `{policyID}{assetNameHex}` — concatenated with no separator.

***

### Phase 1 - Environment Setup (WSL + Ubuntu)

> Skip this phase if you already have Linux.

#### Install WSL on Windows

Open **PowerShell as Administrator** and run:

```powershell
wsl --install
```

Restart your computer when prompted.

After restart, Ubuntu will open and ask you to create a UNIX user.

> ⚠️ Your UNIX username **must be lowercase**. Ubuntu will reject uppercase usernames.

#### Verify WSL is working

Open the Ubuntu terminal and run:

```bash
whoami
```

You should see your lowercase username.

> 💡 **Accessing Windows files from Ubuntu:** Your Windows drives are mounted at `/mnt/c/`, `/mnt/d/` etc. Example: `C:\Users\YourUsername\Desktop\` → `/mnt/c/Users/YourUsername/Desktop/`

***

### Phase 2 - Install token-metadata-creator

In your Ubuntu terminal:

```bash
# Update package list and install dependencies
sudo apt update && sudo apt install git wget -y

# Create working directory
mkdir ~/tokenreg && cd ~/tokenreg

# Download the tool (check for latest release at https://github.com/input-output-hk/offchain-metadata-tools/releases)
wget https://github.com/input-output-hk/offchain-metadata-tools/releases/download/v0.4.0.0/token-metadata-creator.tar.gz

# Extract
tar -xzf token-metadata-creator.tar.gz

# Move to system path
sudo mv token-metadata-creator /usr/local/bin/

# Verify installation
token-metadata-creator --help
```

If you see the help output, the tool is installed correctly.

***

### Phase 3 - Prepare Your Policy Keys

You need two files in your working directory:

#### policy.script

Create this file with your policy script. Example for a simple signature policy:

```bash
cat > ~/tokenreg/policy.script << 'EOF'
{
  "type": "all",
  "scripts": [
    {
      "type": "sig",
      "keyHash": "YOUR_KEY_HASH_HERE"
    }
  ]
}
EOF
```

#### policy.skey

This is your **signing key** — the private key used to prove ownership of the policy. It should be in the Cardano key envelope format:

```json
{
  "type": "PaymentSigningKeyShelley_ed25519",
  "description": "Payment Signing Key",
  "cborHex": "5820YOUR_CBOR_HEX_HERE"
}
```

> 💡 **Finding your keyHashes:** Go to your NFT Project - click "Project Info" in the side bar - open "Cardano Policy" and press "Export Policy Keys".&#x20;
>
> ⚠️ **Security:** Never share your `.skey` file. It proves ownership of your policy and could be used to sign transactions.

Create the policy skripts and copy both files to your working directory:

```bash
cp /path/to/policy.script ~/tokenreg/
cp /path/to/policy.skey ~/tokenreg/
```

***

### Phase 4 - Prepare Your Logo

This phase is the most error-prone. Follow carefully.

#### Requirements

| Property    | Requirement                                             |
| ----------- | ------------------------------------------------------- |
| Format      | **PNG only** (not JPEG, WebP, SVG)                      |
| Dimensions  | Maximum 200×200 pixels (recommended)                    |
| Base64 size | **Must be under 65,536 characters** when base64-encoded |
| Color mode  | RGBA recommended (supports transparency)                |

> ⚠️ **Common mistake:** Many token logos exist online as JPEG or WebP. Even if the file is named `.png`, it might be a JPEG. Always verify and convert if needed.

#### Step 1 - Check your image format

```bash
python3 -c "
with open('your_logo.png', 'rb') as f:
    header = f.read(8)
print('Format:', 'PNG' if header[:8] == b'\\x89PNG\\r\\n\\x1a\\n' else 'NOT PNG - header: ' + header[:4].hex())
"
```

#### Step 2 - Convert and resize to 200×200 PNG

Install Pillow if needed:

```bash
pip install Pillow --break-system-packages
```

Run the conversion:

```python
python3 << 'EOF'
from PIL import Image
import io, base64

# Open your image (works with JPEG, WebP, PNG, etc.)
img = Image.open('your_logo_original.png')
print(f"Original: {img.format}, {img.size}, mode={img.mode}")

# Convert to RGBA (supports transparency)
img = img.convert('RGBA')

# Resize to 200x200
img = img.resize((200, 200), Image.LANCZOS)

# Save as PNG
buf = io.BytesIO()
img.save(buf, format='PNG', optimize=True)
png_data = buf.getvalue()

# Check base64 size
b64 = base64.b64encode(png_data).decode('ascii')
print(f"PNG size: {len(png_data):,} bytes")
print(f"Base64 length: {len(b64):,} chars (limit: 65,536)")
print(f"Within limit: {len(b64) <= 65536}")

# Save
with open('logo.png', 'wb') as f:
    f.write(png_data)
print("Saved as logo.png")
EOF
```

#### Step 3 - Verify the logo

```python
python3 << 'EOF'
import base64, struct

with open('logo.png', 'rb') as f:
    data = f.read()

b64 = base64.b64encode(data).decode('ascii')
print(f"Valid PNG: {data[:8].hex() == '89504e470d0a1a0a'}")
w = struct.unpack('>I', data[16:20])[0]
h = struct.unpack('>I', data[20:24])[0]
print(f"Dimensions: {w}x{h}")
print(f"Base64 length: {len(b64)} chars")
print(f"PNG size: {len(data):,} bytes")
EOF
```

All checks should pass before proceeding.

> 💡 **If you exceed 65,536 base64 chars:** Try reducing to 150×150 or increase PNG compression. The base64 length is roughly 1.37× the PNG byte size.

***

### Phase 5 - Create the Metadata Entry

Set your subject as an environment variable (saves typing):

```bash
export SUBJECT="YOUR_POLICY_ID_HEX_YOUR_ASSET_NAME_HEX"
# Example:
# export SUBJECT="1674af42f614956176bab5d8ac573afb42c13ad89fc8dd0651f5cb8b66534c5652"
```

Navigate to your working directory:

```bash
cd ~/tokenreg
```

Initialize the draft:

```bash
token-metadata-creator entry --init $SUBJECT
```

This creates a `.json.draft` file.

Add all metadata fields in one command:

```bash
token-metadata-creator entry $SUBJECT \
  --name "YourTokenName" \
  --description "Your token description here. Be precise and informative." \
  --ticker "TICKER" \
  --url "https://yourwebsite.com" \
  --logo logo.png \
  --decimals 6 \
  --policy policy.script
```

> ⚠️ **Critical field notes:**
>
> * `--logo` expects a **PNG file path**, not a base64 string. The tool encodes it internally.
> * `--decimals` must match exactly what was set during minting. **Double-check this value!**
> * `--ticker` maximum 9 characters.
> * `--url` must be a valid HTTPS URL.

***

### Phase 6 - Sign and Finalize

Sign the entry with your policy signing key:

```bash
token-metadata-creator entry $SUBJECT -a policy.skey
```

This adds cryptographic signatures to all attestable fields.

Finalize (produces the final JSON):

```bash
token-metadata-creator entry $SUBJECT --finalize
```

This outputs: `{SUBJECT}.json`

***

### Phase 7 - Verify the JSON

**Never skip this step.** Run this verification before submitting:

```bash
cat ${SUBJECT}.json | python3 -c "
import json, sys, base64, struct

d = json.load(sys.stdin)

# Check all required fields
print('subject:', d.get('subject', 'MISSING'))
print('name:', d['name']['value'])
print('ticker:', d['ticker']['value'])
print('decimals:', d['decimals']['value'])
print('url:', d['url']['value'])
print('policy:', d.get('policy', 'MISSING'))

# Check logo
b64 = d['logo']['value']
png = base64.b64decode(b64)
valid_png = png[:8].hex() == '89504e470d0a1a0a'
w = struct.unpack('>I', png[16:20])[0]
h = struct.unpack('>I', png[20:24])[0]
print(f'Logo b64 length: {len(b64)} chars (limit: 65,536)')
print(f'Logo valid PNG: {valid_png}')
print(f'Logo dimensions: {w}x{h}')
print(f'Logo PNG size: {len(png):,} bytes')

# Check signatures exist
for field in ['name', 'ticker', 'decimals', 'url', 'logo', 'description']:
    sigs = len(d.get(field, {}).get('signatures', []))
    print(f'  {field}: {sigs} signature(s)')

# File size
import os
size = os.path.getsize(f'{d[\"subject\"]}.json')
print(f'File size: {size:,} bytes (limit: 370,000)')
"
```

**Expected output:**

* Valid PNG: True
* Dimensions: 200x200
* Base64 length under 65,536
* All fields have 1+ signatures
* File size under 370,000 bytes

> 💡 **Visually inspect the logo too!** Decode it and open it:
>
> ```bash
> python3 -c "
> import json, base64
> with open('${SUBJECT}.json') as f:
>     d = json.load(f)
> with open('/tmp/preview_logo.png', 'wb') as f:
>     f.write(base64.b64decode(d['logo']['value']))
> print('Saved to /tmp/preview_logo.png')
> "
> # Copy to Windows Desktop to view
> cp /tmp/preview_logo.png /mnt/c/Users/YOURNAME/Desktop/
> ```

***

### Phase 8 - Submit the Pull Request

#### Step 1 - Fork the registry

Go to <https://github.com/cardano-foundation/cardano-token-registry> and click **Fork → Create fork**.

Settings:

* Owner: your GitHub account
* Repository name: `cardano-token-registry` (keep default)
* Copy master branch only: checked
* Description: leave empty

#### Step 2 - Create a GitHub Personal Access Token

Go to <https://github.com/settings/tokens> → **Generate new token (classic)**

Settings:

* Note: `Cardano Token Registry`
* Expiration: 30 days (sufficient)
* Scope: tick **repo** only

Click **Generate token** and **copy it immediately** — GitHub shows it only once.

#### Step 3 - Clone your fork

```bash
git config --global user.email "your@email.com"
git config --global user.name "YourGitHubUsername"

git clone https://github.com/YOURUSERNAME/cardano-token-registry.git
cd cardano-token-registry
```

When prompted for password, paste your **Personal Access Token** (not your GitHub password).

#### Step 4 - Create a branch, add file, commit, push

```bash
# Create branch
git checkout -b add-YOURTOKEN-token

# Copy your finalized JSON
cp ~/tokenreg/${SUBJECT}.json mappings/

# Verify it's there
ls mappings/${SUBJECT}.json

# Stage, commit, push
git add mappings/${SUBJECT}.json
git commit -m "Add YOURTOKEN token metadata"
git push origin add-YOURTOKEN-token
```

#### Step 5 - Open the Pull Request

Go to the URL shown in the terminal output (or navigate to your fork on GitHub).

**PR description template:**

```
Adding metadata for the YOURTOKEN token.

Policy ID: YOUR_POLICY_ID
Ticker: YOURTICKER
Decimals: X
URL: https://yourwebsite.com
```

#### Step 6 - Wait for CI and review

After submitting, two automated checks will run:

* **CI / approve** — checks PR format
* **CI / Validate-Metadata** — validates your JSON structure and signatures

Both must pass. The "Merging is blocked" message is normal — a Cardano Foundation maintainer must approve and merge. This typically takes a few days to a week.

***

### Registry Field Reference

| Field         | Required       | Max Length          | Notes                       |
| ------------- | -------------- | ------------------- | --------------------------- |
| `subject`     | ✅              | —                   | policyID + assetNameHex     |
| `name`        | ✅              | 50 chars            | Human-readable token name   |
| `description` | ✅              | 500 chars           | What the token represents   |
| `ticker`      | Optional       | 9 chars             | Trading ticker symbol       |
| `url`         | Optional       | 250 chars           | Must be HTTPS               |
| `logo`        | Optional       | 65,536 base64 chars | PNG only, max 200×200px     |
| `decimals`    | 6 is standard  | —                   | Integer, must match minting |
| `policy`      | Auto-generated | —                   | CBOR-encoded policy script  |

***

### Common Mistakes & How to Avoid Them

#### Wrong decimals value

**Problem:** Setting decimals to `0` when the token was minted with `6` decimals (or any other value).

**Fix:** Always verify your decimals **before** creating the metadata. Check your minting transaction or NMKR/minting platform settings. Decimals must match exactly what was encoded at mint time.

***

#### Logo not a valid PNG

**Problem:** Logo file is actually a JPEG or WebP even though it has a `.png` extension. This produces a broken or corrupted logo in the registry.

**Fix:** Always check the file header bytes. If the first 8 bytes are not `89 50 4E 47 0D 0A 1A 0A`, it is not a PNG. Convert explicitly with Pillow as shown in Phase 4.

***

#### Logo base64 too large

**Problem:** A 256×256 PNG may exceed the 65,536 base64 character limit.

**Fix:** Use 200×200 maximum. Check the base64 length before proceeding. Reduce to 150×150 if still too large.

***

#### Logo path vs base64 confusion

**Problem:** Passing a pre-encoded base64 file to `--logo` instead of the PNG file path.

**Fix:** The `--logo` flag expects a **PNG file path**. The tool encodes it to base64 internally. Example: `--logo logo.png` ✅ not `--logo logo.b64` ❌

***

#### WSL username with uppercase

**Problem:** Trying to create a UNIX user like `Flo` — Ubuntu rejects it.

**Fix:** Always use lowercase for UNIX usernames: `flo`, `john`, `tokenadmin`.

***

#### Using GitHub password instead of Personal Access Token

**Problem:** `git push` fails with authentication error.

**Fix:** GitHub no longer accepts passwords for CLI operations. Generate a Personal Access Token with `repo` scope and use that as the password.

***

#### Not visually verifying the logo

**Problem:** The JSON passes all automated checks (valid PNG bytes, correct dimensions) but the actual image is broken or wrong.

**Fix:** Always decode the base64 logo from your final JSON and visually inspect it before submitting the PR. Open it in an image viewer to confirm it looks correct.

***

#### Subject lowercase requirement

**Problem:** The filename must be `{subject}.json` where subject is all lowercase hex.

**Fix:** Policy IDs and asset name hex are always lowercase. The `token-metadata-creator` tool handles this automatically.

***

### Best Practices Checklist

Before submitting your PR, confirm all of the following:

**Metadata correctness:**

* Subject is correct: policyID + assetNameHex (no separator)
* Decimals match exactly what was set at minting time
* Description is accurate and informative (max 500 chars)
* URL is live and HTTPS
* Ticker is correct (max 9 chars)

**Logo:**

* File is a true PNG (verified by header bytes)
* Dimensions are 200×200 or smaller
* Base64 length is under 65,536 characters
* Visually inspected and looks correct
* RGBA mode for transparency support

**Signatures:**

* Signed with the correct policy.skey
* All fields have signatures
* Policy field is present

**File:**

* JSON filename matches subject exactly
* File is in the `mappings/` directory
* File size is under 370KB
* JSON is valid (no syntax errors)

**GitHub:**

* Forked from `cardano-foundation/cardano-token-registry`
* PR targets `master` branch
* Both CI checks pass
* PR description includes policy ID, ticker, decimals

***

### Cleanup After Submission

Once your PR is submitted, you can safely clean up your local files:

```bash
# Remove working files
rm -rf ~/tokenreg
rm -rf ~/cardano-token-registry

# Remove downloaded tool archive
rm -f ~/token-metadata-creator.tar.gz*

# Return to home directory
cd ~
```

The `token-metadata-creator` binary remains installed at `/usr/local/bin/` — useful if you register more tokens in the future.
