Skip to content

novaoc/ordinal-verify-bot

Repository files navigation

Ordinal Verify Bot

Discord bot that proves a user controls a Bitcoin address — and lists any Ordinal inscriptions held there — using BIP-322 signed messages.

How it works

  1. User runs /verify address:<btc_addr>. The bot returns a single-use, 5-minute challenge string bound to the user's Discord ID, the supplied address, and a fresh random nonce.
  2. User signs the challenge in their wallet (Sparrow / Leather / Xverse / Unisat / any BIP-322-capable wallet).
  3. User runs /submit signature:<base64>. The bot:
    • Verifies the signature with the Rust-backed bip322 library (verify_simple_encoded).
    • Records the discord_id ↔ btc_address binding in SQLite.
    • Looks up inscriptions held by the address via the Ordiscan API.
    • Evaluates holder-rules (see below) and grants matching roles.
    • Shows a link to a mobile-friendly signing page (Sats Connect) if configured.

All bot replies are ephemeral, so addresses and signatures are never visible to other channel members.

New Features (PR #5 & PR #6)

API Migration (PR #5)

  • Migrated from deprecated Hiro Ordinals API to Ordiscan API
  • Free-tier-friendly with no rate limits for basic usage

Holder-Rule System (PR #5)

  • Grant Discord roles automatically based on which inscriptions a user holds
  • Three match types:
    • inscription: Specific inscription ID
    • parent: Covers entire collection (children of a parent inscription)
    • meta_collection: Ordinals Wallet slug for pre-Sept-2023 OG collections
  • New slash commands:
    • /rule_add type:<inscription|parent|collection|meta_collection> value:<id|slug> role:@role
    • /rule_remove rule_id:<n>
    • /rule_list
    • /rule_help

Mobile-Friendly Signing (PR #5)

  • Added Sats Connect integration
  • When SIGN_PAGE_URL is configured, /verify shows a button linking to a standalone signing page with challenge pre-filled
  • Opens user's wallet app directly for seamless mobile verification

Role Synchronization (PR #6)

  • Added /sync_me — any verified user can re-sync their own roles
  • Added /sync_roles — admins can sync one member or all verified members
  • Keeps roles in sync with current holdings after buys/sells
  • Safe by design:
    • Only strips roles granted by holder-rules or the verified role
    • Skips members on API errors (never strips on transient failures)
    • Respects bot's role hierarchy (won't strip roles above bot's top role)
    • Idempotent — running twice does nothing if holdings unchanged

Documentation & Compliance

  • Added PRIVACY.md — Privacy Policy
  • Added TERMS.md — Terms of Service

Testing

  • Added extensive test suite:
    • tests/bip322_sign.py — BIP-322 signature generation for tests
    • tests/test_bot_logic.py — Core bot logic tests
    • tests/test_inscriptions.py — Ordiscan API integration tests
    • tests/test_role_rules.py — Holder-rule system tests
    • tests/test_verifier.py — BIP-322 signature verification tests

Commands

Command What it does
/verify address:<btc_addr> Issue a fresh signing challenge.
/submit signature:<base64> Submit the BIP-322 signature for the active challenge.
/whoami List the addresses you've verified.
/rule_add Bind a Discord role to ownership of an inscription, parent, or collection.
/rule_remove Delete a holder-rule by its rule_id.
/rule_list Show all holder-rules configured for this server.
/rule_help Guide to setting up holder-rules.
/sync_me Re-sync your roles based on current holdings.
/sync_roles [@member] Re-sync roles for one member or all verified members (admin only).

Setup

git clone https://github.com/novaoc/ordinal-verify-bot.git
cd ordinal-verify-bot
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env  # then edit .env
python ordinal_verify_bot.py

Discord application

  1. Create an application + bot at https://discord.com/developers/applications.
  2. Copy the bot token into DISCORD_TOKEN in .env.
  3. Invite the bot with the applications.commands scope (and bot scope with Manage Roles only if you want VERIFIED_ROLE_ID to work).

Configuration

  • DISCORD_TOKEN — Bot token from Discord developer portal.
  • GUILD_ID — Register slash commands to one guild for instant updates. Leave unset for global commands (propagation can take up to ~1 hour).
  • VERIFIED_ROLE_ID — Role granted on successful verification. The bot's top role must sit above this role.
  • ORDISCAN_API — Ordiscan API endpoint (default: https://api.ordiscan.com).
  • ORDISCAN_API_KEY — API key (required — without one the bot still verifies ownership but always reports 0 inscriptions). Get a free key at https://ordiscan.com/docs/api/login.
  • DB_PATH — SQLite database path (default: verifications.db).
  • SIGN_PAGE_URL — Optional URL of the static "Sign with wallet" page. When set, /verify shows a link button that opens this page with the challenge pre-filled, so mobile users don't have to copy the message into their wallet manually. Leave blank to omit the button.

Security notes

  • The challenge message binds discord_id, btc_address, and a 128-bit random nonce, so signatures cannot be replayed across users or addresses.
  • Nonces are single-use and expire after 5 minutes; pending challenges are held in memory only and cleared on bot restart.
  • Slash command responses use ephemeral=True so signatures aren't broadcast.
  • The bot uses discord.Intents.default() only — no privileged intents.

License

MIT

Documentation

  • Privacy Policy: PRIVACY.md
  • Terms of Service: TERMS.md

About

Discord bot for verifying Bitcoin Ordinal NFT ownership using BIP322 signatures

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages