Open Crown Standard

Version: 1.0.0 License: CC-BY-4.0 (Attribution 4.0 International) Status: Released Last Updated: May 2026


Abstract

The Open Crown Standard defines a specification for registering and verifying ownership of cryptocurrency ticker symbols across multiple blockchains. Unlike wiki-style token databases, this standard requires cryptographic proof of ownership before registration, creating a trustless source of truth for "who owns this ticker on this chain."

The standard introduces four core entities (Symbol, Representation, Crown, Organization), a verification-first trust model, and an anti-squatting mechanism called Heartbeat that decays inactive registrations.


Table of Contents

  1. Introduction
  2. Terminology
  3. Core Entities
  4. Verification Methods
  5. Trust Model
  6. Heartbeat Oracle
  7. Anti-Squatting Mechanisms
  8. Metadata Schemas
  9. API Specification
  10. Implementation Requirements
  11. Security Considerations
  12. Governance
  13. License

1. Introduction

1.1 Problem Statement

The cryptocurrency ecosystem lacks a canonical answer to: "Who owns this ticker symbol on this chain?"

When someone says "buy DOGE," they could mean:

Current solutions either:

1.2 Solution

The Open Crown Standard provides:

  1. Verification-first registration - Prove ownership cryptographically before claiming
  2. Soulbound identity - Registrations (Crowns) cannot be transferred or sold
  3. Cross-chain aggregation - One Symbol entity unifies all chain representations
  4. Anti-squatting - Heartbeat decay removes inactive registrations
  5. Decentralized trust - No admin can grant or revoke registrations

1.2.1 What Operators Can Build

The Open Crown Standard provides the verification foundation. Operators can build:

Service Description
Profile Hosting Token profiles with verified ownership badges
Listing Aggregation Export to TokenList format for DEXs and wallets
Holder Communications Verified project-to-holder messaging
Deal Facilitation Transparent ownership transfers with notice periods
Social Verification Link verified social accounts to crowns
Gateway Services ENS/SNS resolution, domain linking

These services transcend any single operator—verification is on-chain, metadata is portable (IPFS), and users can switch operators freely.

1.3 Design Principles

Principle Implementation
Trustless On-chain verification, no admin override
First-claim Earliest verified claimant wins
Soulbound No transfers, prevents speculation
Active Heartbeat decay removes squatters
Open CC-BY-4.0 license, anyone can implement
Peer-based All crowns are equal, no hierarchy

1.4 Protocol vs Operator Model

The Open Crown Standard separates protocol concerns from operational concerns:

Protocol = The Open Crown Standard + reference contracts + governance process

Operators = Services that implement and serve the protocol to end users

1.4.1 Terminology

Term Definition
Open Crown Standard This specification document defining the protocol
OpenCrown The overall ecosystem implementing this standard
OCP Open Crown Proposal - the process for proposing changes
Operator A service implementing the standard for end users
OpenCrown DAO The governance body for the protocol

1.4.2 Operator Capabilities

Operators MAY:

1.4.3 Operator Restrictions

Operators MUST NOT:

1.4.4 Trust Model

Users trust the protocol (contracts + standard) not the operator:

┌─────────────────────────────────────────────────────────────┐
│                    PROTOCOL LAYER                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │ Open Crown  │  │  Reference  │  │ Governance  │        │
│  │  Standard   │  │  Contracts  │  │   (OCPs)    │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
└─────────────────────────────────────────────────────────────┘
                            │
                    Implements & Serves
                            │
┌─────────────────────────────────────────────────────────────┐
│                    OPERATOR LAYER                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │ Operator A  │  │ Operator B  │  │ Operator C  │        │
│  │  (UI + API) │  │  (UI + API) │  │  (API only) │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
└─────────────────────────────────────────────────────────────┘

This separation ensures:

1.4.5 Operator Deal Requirements

When facilitating deals (ownership transfers), operators MUST:

These are minimum requirements. Operators may implement stricter policies (longer holding periods, additional verification, community review) but cannot relax these minimums.


2. Terminology

2.0 Ecosystem Terms

Term Definition
Open Crown Standard This specification document defining the protocol
OpenCrown The overall ecosystem implementing this standard
OCP Open Crown Proposal - the formal process for proposing changes to the standard
Operator A service implementing the standard to serve end users
OpenCrown DAO The governance body overseeing the protocol and standard

2.1 Protocol Terms

Term Definition
Symbol A ticker string (e.g., "DOGE") aggregating all chain representations
Representation A specific token instance on a specific chain (e.g., DOGE on Arbitrum)
Crown A soulbound NFT proving ownership of a Symbol on a specific chain
Organization An entity (foundation, company) that can attest to native coin ownership
Heartbeat Activity score (0-100) determining registration health
Verification Cryptographic proof of token ownership
Claimant Address attempting to register a Crown
Manager Delegated address with Crown management rights (set maintainer, update metadata). Cannot initiate deals.
Maintainer Delegated address with metadata-only management rights (update metadata URI). Set by owner or manager.
Linked Crowns Crowns for the same symbol owned by the same wallet, sharing metadata

2.2 Chain Identifier Format

Chains are identified using CAIP-2 format:

<namespace>:<reference>

Examples:
- eip155:1        (Ethereum Mainnet)
- eip155:42161    (Arbitrum One)
- eip155:8453     (Base)
- solana:mainnet  (Solana Mainnet)
- solana:devnet   (Solana Devnet)

2.3 Symbol Format

Symbols have no universal format restrictions at the protocol level. Each chain may impose its own constraints (see Appendix D: Chain-Specific Constraints).

Implementations should:

This approach maximizes flexibility while ensuring users understand the trade-offs of their symbol choices.

2.4 Crown Identifier

Crowns are displayed using this format:

<symbol>.<chain-suffix>

Examples:
- DOGE.arb    (DOGE on Arbitrum)
- DOGE.sol    (DOGE on Solana)
- DOGE.eth    (DOGE on Ethereum)
- DOGE.base   (DOGE on Base)

Uniqueness: Crowns are uniquely identified by the combination of symbol + chainId + tokenAddress. Multiple crowns may share the same display identifier (DOGE.arb) if they represent different token contracts on the same chain. The tokenAddress is the distinguishing factor.

Resolution: When multiple crowns exist for the same symbol on the same chain, operators must return all matching crowns. See Appendix F for resolution patterns and Appendix G for ENS/SNS naming conventions.

On-Chain Verification: Users can verify operators are returning all crowns by calling getCrownCount(symbol, chainId) on the registry contract and comparing with the operator's returned list length.

2.4.1 Crown Uniqueness Model

All chains use the same 3-tuple uniqueness key: (symbol, chainId, tokenAddress). Multiple crowns can exist for the same symbol on the same chain if they represent different token contracts. For example, three different tokens all using the "DOGE" ticker on Arbitrum would each have their own crown.

EVM (eip155): The registry uses keccak256(symbol, chainId, tokenAddress) as the storage key. getCrownCount(symbol, chainId) returns the number of active crowns for a given symbol on a chain.

Solana: Crown PDAs use the seed ["crown", symbol, chain_id, token_mint]. The CrownCount PDA (["crown_count", symbol, chain_id]) aggregates the active crown count per symbol per chain, equivalent to the EVM's getCrownCount.

DNS analogy: Think of symbols as domain names, chains as TLDs (.eth, .sol, .arb), and token addresses as specific servers behind a domain. Multiple servers (contracts) can share one domain (ticker) on any chain — the protocol never picks winners.

2.5 CAIP Standards

The Open Crown Standard builds on Chain Agnostic Improvement Proposals (CAIPs) for cross-chain interoperability:

Standard Purpose Example
CAIP-2 Chain identification eip155:1 (Ethereum Mainnet)
CAIP-10 Account identification eip155:8453:0x1234... (Base)
CAIP-19 Asset identification eip155:137/erc20:0x1234... (Polygon)

CAIP-2 (Chain IDs): Used throughout the standard for chain identification. All chain references use the namespace:reference format.

CAIP-10 (Accounts): Used for cross-chain account identification when linking crowns across chains.

CAIP-19 (Assets): Used for unambiguous asset identification in token lists and cross-chain references.

See also: EIP (Ethereum Improvement Proposals) for Ethereum-specific standards referenced in this document.


3. Core Entities

3.1 Symbol Entity

The Symbol entity aggregates all representations of a ticker across chains.

interface Symbol {
  // Identifier
  ticker: string;                    // e.g., "DOGE"

  // Aggregated data
  representations: Representation[]; // All chain instances
  totalMarketCap: bigint;           // Sum across chains
  totalVolume24h: bigint;           // Sum across chains
  totalHolders: number;             // Sum across chains

  // Metadata
  displayName?: string;             // e.g., "Dogecoin"
  description?: string;
  website?: string;
  organization?: OrganizationId;    // If attested by foundation

  // Linking
  linkedCount: number;              // Number of linked crowns

  // Timestamps
  firstRegistered: timestamp;       // Earliest Crown mint
  lastActivity: timestamp;          // Most recent committed epoch's leaf (off-chain)
}

Derivation: Symbols are derived (not stored) by aggregating Crown events. Implementations should use direct RPC queries or a caching layer to maintain Symbol state.

3.2 Representation Entity

A Representation is a specific token on a specific chain.

interface Representation {
  // Identifier
  symbol: string;                   // Parent Symbol ticker
  chainId: ChainId;                 // CAIP-2 chain identifier
  tokenAddress: Address;            // Contract/mint address

  // Token data
  name: string;
  decimals: number;
  totalSupply: bigint;

  // Market data
  marketCap: bigint;
  volume24h: bigint;
  holders: number;
  liquidity: bigint;

  // Registration
  crown?: CrownId;                  // If registered
  status: RepresentationStatus;

  // Timestamps
  deployedAt?: timestamp;
  registeredAt?: timestamp;
}

enum RepresentationStatus {
  AVAILABLE = "available",          // No token exists
  UNREGISTERED = "unregistered",    // Token exists, no Crown
  GRACE_PERIOD = "grace_period",    // New registration, <30 days
  REGISTERED = "registered",        // Crown minted, active
  INACTIVE = "inactive"             // Reclaimed via verifyAndInactivate
}

3.3 Crown Entity

A Crown is a soulbound NFT proving ownership of a Symbol on a chain. All crowns for the same symbol are equal peers with no hierarchy.

A Crown's state is split across three locations:

Layer What lives here
On-chain Crown record Identity, verification commitment, mint timestamp, owner, metadata CID
On-chain registry slots Deal state, delegation roles, peer references, heartbeat Merkle roots
Off-chain (operator API) Heartbeat score, metadata JSON (IPFS), aggregations, derived views

3.3.1 On-Chain Crown Record

The Crown record stored by a conforming registry contract:

// On-chain.
// EVM: stored in OpenCrownRegistry, keyed by ERC-721 tokenId.
// Solana: stored in the Crown PDA derived from
//         ["crown", symbol, chain_id, token_mint].
interface Crown {
  tokenId: bigint;                  // ERC-721 tokenId (EVM); global_index (Solana)
  symbol: string;                   // Token ticker (max 20 chars)
  chainId: ChainId;                 // Chain where the token lives
  tokenAddress: Address;            // Token contract address
  symbolType: SymbolType;           // Native, Token, Wrapped, Bridged, Synthetic
  verificationMethod: VerificationType;
  verificationProofHash: bytes32;   // keccak256 of proof data
  mintTimestamp: timestamp;         // Unix timestamp of mint
  owner: Address;                   // ERC-721 owner; soulbound (transfers revert)
  metadataCid: bytes32;             // IPFS CID of profile metadata JSON
}

enum SymbolType {
  NATIVE = 0,     // Native blockchain coin (ETH, SOL)
  TOKEN = 1,      // Standard fungible token
  WRAPPED = 2,    // Wrapped version (WETH, wSOL)
  BRIDGED = 3,    // Cross-chain bridge token
  SYNTHETIC = 4   // Synthetic/derivative token
}

Deal state, delegation, peer references, and the heartbeat Merkle commitments are stored in separate on-chain slots, queried via dedicated read functions:

3.3.2 Crown View (Operator API)

The operator API joins on-chain Crown state with off-chain data into a single response shape clients can render:

// CrownView, response shape for GET /crown/{id}.
interface CrownView extends Crown {
  // From DealState (§7.5)
  ownerSince: timestamp;
  dealInitiatedAt?: timestamp;
  dealBuyer?: Address;

  // From delegation (§3.5)
  manager?: Address;
  maintainer?: Address;

  // From peer references (§3.4a)
  isLinked: boolean;                // True if peer references exist for this Crown

  // From Heartbeat Oracle (§6). Operator-computed off-chain, served
  // from the latest committed epoch, provable on-chain via Merkle
  // proof against the per-epoch root.
  heartbeat: number;                // 0-100 composite score
  heartbeatUpdatedAt: timestamp;    // committedAt of the source epoch
  heartbeatEpoch: number;           // Source epoch number
  atRiskDays: number;               // Consecutive at-risk-day count
  gracePeriodEnds?: timestamp;      // mintTimestamp + GRACE_PERIOD

  // Profile metadata, fetched from IPFS using metadataCid
  metadataUri: string;
  releasedAt?: timestamp;           // Set when CrownReleased fires
}

Soulbound: Crowns implement ERC-5192 (Minimal Soulbound NFTs). Transfer functions must revert.

Peer Model: All crowns for a symbol are equal peers. Multiple crowns can exist for the same symbol on the same chain, each representing a different token contract (distinguished by tokenAddress). Prominence is derived from market data, not registration order.

Active count: Registries expose getCrownCount(symbol, chainId) so a client can verify that an operator is returning all matching crowns (§4.5).

3.4 Crown Linking (Peer-Based)

When the same wallet claims the same symbol on multiple chains, those crowns become linked peers sharing metadata.

// Linking is determined by:
// 1. Same owner wallet address
// 2. Same symbol (case-insensitive)
// 3. Different chains

interface LinkedCrowns {
  wallet: Address;
  symbol: string;
  crowns: Crown[];                  // All crowns for this wallet+symbol
  sharedMetadataCid: string;        // IPFS CID shared across all linked crowns
}

Key Properties:

3.4a Peer References (On-Chain)

Section 3.4 describes the peer relationship as an observable fact (same owner + same symbol across chains). Peer References turn that relationship into first-class on-chain state so third-party integrators (indexers, block explorers, wallets) can resolve the peer graph without trusting any single operator's off-chain database.

Each Registry maintains a per-crown list of pointers to sibling crowns on other chains. Writes are gated by the crown owner OR a designated PEER_OPERATOR_ROLE holder — the latter exists so the operator that drives chain expansion can keep every sibling crown's list in sync in one batch per chain, without forcing the user to sign N transactions across N chains.

// Each entry describes one sibling crown living on another chain (or another
// mint on the same chain — interpretation is driven by `chainId`).
interface CrownPeer {
  chainId:     uint64;    // Chain where the peer crown lives (CAIP-2 numeric; 0 = Solana)
  peerCrownId: uint256;   // Peer crown identifier on its own registry.
                          //   EVM: ERC-721 tokenId
                          //   Solana: Pubkey of the crown PDA, padded to 32 bytes
  kind:        uint8;     // 0 = canonical peer, 1 = bridge/wrapped, 2 = migration, 3..255 reserved
  addedAt:     uint64;    // Block timestamp when the reference was added
}

Semantics:

Relationship to Section 3.4: Linking (§3.4) describes the existence of the peer relationship. Peer References (§3.4a) make it queryable on-chain — integrators who read getPeers(tokenId) on any single registry can discover the full sibling set without consulting an off-chain API.

Implementation: See §10.2 (events), §10.3 (errors), §10.4 (roles), and the per-namespace guidance in namespaces/evm/README.md and namespaces/solana/README.md.

3.5 Crown Delegation Model

Crown owners can delegate management rights to other addresses. The delegation hierarchy is:

Owner > Manager > Maintainer

Roles and Permissions:

Permission Owner Manager Maintainer
Set manager Yes No No
Remove manager Yes No No
Set maintainer Yes Yes No
Remove maintainer Yes Yes No
Update metadataUri Yes Yes Yes
Update metadataCid Yes Yes Yes
Set canonical peer Yes No No
Clear canonical peer Yes No No
Add / remove peer (incl. attested) Yes (or PEER_OPERATOR_ROLE) No No
Initiate deal Yes No No
Cancel deal Yes No No
Execute deal (seller side) Yes No No
Release crown Yes No No

Operator-mediated permissions (off-chain):

The protocol does not constrain off-chain operator features, but implementations should mirror the on-chain hierarchy. The reference operator (Chain Daddy) treats manager-or-greater as the gate for the polished publish pipeline (DB-backed metadata pipelines, holder broadcasts, tier criteria, gated links, polls, tip links, discord guild and rules, snapshot space link, community uploads, branding, crown app widgets, LP-lock, trading-pair links). Maintainer is on-chain metadata-only — operators may keep maintainer out of the polished pipeline so the polished UX remains consistent with the deeper trust granted to a manager.

Canonical-peer + attestation surface:

Delegation Rules:

3.6 Organization Entity

Organizations are verified entities that can attest to native coin ownership.

interface Organization {
  // Identifier
  id: string;                       // e.g., "dogecoin-foundation"

  // Verification
  domain: string;                   // e.g., "dogecoin.com"
  domainVerified: boolean;          // DNS TXT record check
  verificationRecord?: string;      // TXT record content

  // Profile
  name: string;                     // e.g., "Dogecoin Foundation"
  description?: string;
  logo?: string;                    // IPFS URI

  // Attestations
  attestedSymbols: string[];        // Symbols they've attested

  // Trust
  trustLevel: TrustLevel;
  verifiedBy?: Address;             // Who verified the domain
}

enum TrustLevel {
  UNVERIFIED = 0,       // No verification (not allowed for crowns)
  SELF_ATTESTED = 1,    // Majority holder verification
  VERIFIED = 2,         // Deployer, owner, or mint authority verification
  FOUNDATION = 3,       // Foundation/organization attestation
  GOVERNANCE = 4        // DAO governance vote
}

3.7 Symbol Types

Type Description tokenAddress
native L1/L2 gas token (ETH, SOL, ARB) null
token Deployed contract (ERC-20, SPL) required
wrapped Canonical wrapped version (WETH) required
bridged Cross-chain bridge version required
synthetic Derivative/synthetic asset required

4. Verification Methods

Before claiming a Crown, the claimant must prove ownership of the underlying token.

4.1 Verification Method Hierarchy

Method Strength Description
mint_authority Strongest Claimant controls token minting (Solana)
deployer High Claimant deployed the token contract
factory_creator High Claimant created token via platform factory
owner High Claimant is owner() of the contract
foundation_attestation High Organization attests ownership
governance_vote High DAO governance approved claim
update_authority Medium Claimant controls metadata (Solana)
majority_holder Medium Claimant holds >50% of supply

4.2 EVM Verification

enum VerificationType {
    Deployer,           // msg.sender == deployer address
    Owner,              // IOwnable(token).owner() == msg.sender
    MintAuthority,      // For Solana-bridged tokens
    Foundation,         // Organization attestation
    Governance,         // DAO governance vote
    Majority            // balanceOf(msg.sender) > totalSupply / 2
}

Deployer verification:

function verifyDeployer(address token, address claimant) returns (bool) {
    // Check creation transaction
    bytes32 codeHash = token.codehash;
    // Implementation queries deployment transaction
    return deploymentTx.from == claimant;
}

Owner verification:

function verifyOwner(address token, address claimant) returns (bool) {
    try IOwnable(token).owner() returns (address owner) {
        return owner == claimant;
    } catch {
        return false;
    }
}

Majority holder verification:

function verifyMajorityHolder(address token, address claimant) returns (bool) {
    uint256 balance = IERC20(token).balanceOf(claimant);
    uint256 supply = IERC20(token).totalSupply();
    return balance > supply / 2;
}

4.3 Solana Verification

pub enum SolanaVerificationMethod {
    MintAuthority,      // Claimant is mint authority
    UpdateAuthority,    // Claimant is metadata update authority
    Deployer,           // Claimant deployed the program
    MajorityHolder,     // Claimant holds >50% of supply
    Foundation,         // Organization attestation
}

Mint authority verification:

pub fn verify_mint_authority(
    mint: &Account<Mint>,
    claimant: &Signer
) -> Result<bool> {
    Ok(mint.mint_authority == COption::Some(claimant.key()))
}

4.4 Foundation Attestation

For native coins (BTC, ETH, SOL, DOGE) where no single deployer exists:

  1. Organization registers with verified domain
  2. Organization signs attestation message
  3. Attestation stored on-chain
  4. Crown minted with foundation_attestation method
interface Attestation {
  organization: OrganizationId;
  symbol: string;
  chainId: ChainId;
  tokenAddress: Address;
  beneficiary: Address;           // Who receives the Crown
  signature: bytes;               // Organization's signature
  expiresAt: timestamp;
}

4.5 Verification Proof Structure

interface VerificationProof {
  method: VerificationType;
  chainId: ChainId;
  tokenAddress: Address;
  claimant: Address;

  // Method-specific data
  data: {
    // For deployer
    deploymentTxHash?: bytes32;

    // For owner
    ownershipBlockNumber?: number;

    // For majority_holder
    balance?: bigint;
    totalSupply?: bigint;
    snapshotBlock?: number;

    // For foundation_attestation
    attestation?: Attestation;
  };

  timestamp: number;
}

5. Trust Model

5.1 Verification Flow

+--------------+     +--------------+     +--------------+
|   Verify     |---->|   Commit     |---->|    Claim     |
|  (off-chain) |     |  (on-chain)  |     |  (on-chain)  |
+--------------+     +--------------+     +--------------+
      |                   |                   |
      |                   |                   |
      v                   v                   v
  Check ownership    Store proofHash     Mint Crown
  Build proof        Emit full proof     Reference proof

5.2 Proof Storage

The standard uses a commit-reveal pattern:

  1. On-chain: Store only verificationProofHash = keccak256(proof)
  2. Events: Emit full proof in event logs
  3. Indexer: Reconstruct proofs from events
event CrownMinted(
    uint256 indexed tokenId,
    address indexed owner,
    string symbol,
    uint256 chainId,
    address tokenAddress,
    uint8 symbolType,
    uint8 verificationType,
    bytes32 verificationProofHash,
    bytes proofData                    // Full proof in event
);

5.3 Verification Guarantees

Property Guarantee
Authenticity Proof verified on-chain at claim time
Immutability proofHash cannot be changed after mint
Auditability Full proof available in event logs
Efficiency Only 32 bytes stored on-chain

5.4 Trust Levels

enum TrustLevel {
  UNVERIFIED = 0,       // No verification (not allowed)
  SELF_ATTESTED = 1,    // Majority holder
  VERIFIED = 2,         // Deployer, owner, mint authority
  FOUNDATION = 3,       // Foundation attestation
  GOVERNANCE = 4        // DAO governance vote
}

6. Heartbeat Oracle

Heartbeat is an activity score (0–100) used to detect dormant crowns and reclaim their slots. The score has three dimensions and one composite. Implementations MUST commit per-epoch Merkle roots on-chain and MUST permit permissionless reclaim via Merkle proof.

6.1 Score Dimensions

The composite score is the weighted sum of three dimensions:

composite_score = round(
    timestamp_score * 0.5 +
    volume_score    * 0.3 +
    holder_score    * 0.2
)

Where:
- timestamp_score = stepped decay from time since mint
- volume_score    = log-scaled effective volume (smoothed across 24h + 7d, capped at K × liquidity)
- holder_score    = Holder Health Score (concentration / suspicious / distribution / scale)

timestamp_score (50%) — computed off-chain from the on-chain mintTimestamp:

Crown age timestamp_score
0–30 days 100
30–90 days 90
90–180 days 80
180–365 days 70
365–730 days 60
730+ days 50

volume_score (30%) — observed by the indexer from DEX swap events. Two anti-wash-trading filters run BEFORE the log-scale step:

effective_volume = clamp_liquidity(smooth_volume(volume_24h_usd, volume_7d_usd), liquidity_usd)

volume_score = clamp(
    100 * (log10(effective_volume) - log10(MIN_VOLUME)) /
          (log10(MAX_VOLUME) - log10(MIN_VOLUME)),
    0, 100
)

Where:

Reference parameters (operators MAY tune): MIN_VOLUME = $20/day, MAX_VOLUME = $100,000/day, SMOOTH_W_24H = 0.7, SMOOTH_W_7D_DAILY = 0.3, LIQUIDITY_CAP_MULTIPLIER = 100 (effective volume above 100× pool liquidity is treated as wash-traded).

holder_score (20%) — Holder Health Score — a four-component composite computed from the token's holder distribution.

holder_score = clamp(
      concentration_weight * concentration_factor
    + suspicious_weight    * (1 - suspicious_ratio)
    + distribution_weight  * min(1, wallets_for_half / WALLETS_FOR_HALF_TARGET)
    + scale_weight         * min(1, total_holders / HOLDER_SCALE_TARGET),
    0, 100
)

Sub-component definitions:

Reference component weights (operators MAY tune; weights MUST sum to 100):

Component Default weight Why
concentration 40 Largest dimension. Cannot be gamed by sybils — splitting balances does not change top-10 share.
suspicious 33 Direct penalty for adversarial wallet classes.
distribution 17 Rewards genuinely spread supply.
scale 10 Small bonus, capped quickly so a million-bot crown can't overcome bad shape.

Reference threshold parameters (operators MAY tune): TOP50_ELBOW = 35, WALLETS_FOR_HALF_TARGET = 25, HOLDER_SCALE_TARGET = 5,000 (EVM) / 50,000 (Solana mainnet — Solana token-account semantics produce broader holder distributions).

The score MUST be derived from on-chain mint timestamp, DEX-derived volume, and holder distribution shape only. Operators MAY surface verified social channels as a UI-level trust badge alongside the score, but social signals MUST NOT enter the score computation.

6.2 Reclaim Thresholds

Composite score State Consequence
80–100 Healthy Normal operation
60–79 Warning Operator MAY notify holder
0–59 At-Risk Counts toward at_risk_days

A crown becomes reclaimable when ALL of:

  1. Its current composite score is < AT_RISK_THRESHOLD (60), AND
  2. It has been at-risk for >= AT_RISK_DAYS_REQUIRED consecutive epochs (default: 30), AND
  3. It is past its post-mint GRACE_PERIOD (default: 30 days), AND
  4. It is currently active (not previously reclaimed or released).

Operators MAY tune AT_RISK_THRESHOLD, AT_RISK_DAYS_REQUIRED, and GRACE_PERIOD at deploy time but MUST NOT change them at runtime — these constants are immutable in conforming implementations.

6.3 Grace Period

Newly-minted crowns are unreclaimable for the first GRACE_PERIOD (default: 30 days) regardless of score. The grace period MUST be measured from the original first claim of the (symbol, chainId, tokenAddress) tuple — release-and-remint cycles MUST NOT reset it.

6.4 Per-Epoch Merkle Commitment Model

Each implementation MUST:

6.4.1 Designate a publisher. A single ROOT_PUBLISHER_ROLE (EVM) or root_publisher: Option<Pubkey> (Solana) is empowered to submit per-epoch root commits. The publisher is operator-controlled; reclaim itself is permissionless and does not require this role.

6.4.2 Commit one root per epoch. Each epoch (default: EPOCH_DURATION = 1 day), the publisher computes scores for every active crown, builds a Merkle tree over the leaf set, and commits the root via commitHeartbeatRoot(epoch, root). The contract MUST require epoch >= latestEpoch.

6.4.3 Honor a challenge window. Each commit enters a CHALLENGE_WINDOW (default: 24 hours) during which the publisher MAY recommit the same epoch with a corrected root (operator self-correction path). After the window, the root is finalized and immutable for that epoch. Reclaim MUST NOT consult an unfinalized root.

6.4.4 Bound reclaim staleness. The contract MUST require reclaim proofs to use an epoch within MAX_RECLAIM_STALENESS (default: 2) of latestEpoch. This prevents an attacker from reusing an ancient proof to inactivate a since-recovered crown.

6.4.5 Encode leaves canonically. Indexers and verifiers MUST agree on the leaf encoding. The protocol fixes per chain family:

Chain family Leaf encoding
EVM keccak256(abi.encode(tokenId, score, atRiskDays, epoch))
Solana sha256(crown_pda || score || atRiskDays_le || epoch_le)

The two trees are computed independently per chain. Cross-chain leaf interoperability is out of scope.

6.4.5a Surface a confidence annotation (OPTIONAL). Operators MAY annotate each leaf with a 0..100 confidence value derived from the underlying provider data's freshness at scoring time. The recommended decay model is linear between two breakpoints (e.g. MaxAgeFresh = 1h → 100, MaxAgeStale = 12h → 0). Confidence MUST NOT be part of the leaf hash; it is purely informational metadata surfaced through the proof API so downstream consumers (reclaim bots, dashboards) can decide whether to act on a stale leaf.

6.4.6 Provide a proof endpoint. Operators MUST expose a publicly callable read API:

Endpoint Returns Auth
GET /heartbeat/score/:tokenId latest committed (score, atRiskDays, epoch) none
GET /heartbeat/proof/:tokenId/:epoch Merkle proof for the leaf none
GET /heartbeat/root/latest latest committed (epoch, root, committedAt, finalized) none

The API is a convenience layer, not the source of truth. The on-chain root is authoritative, and any party MAY run their own indexer to produce equivalent proofs. Operators MAY require an API key for write/protected endpoints; read endpoints in this section MUST remain public.

6.5 Reclaim

Anyone MAY call verifyAndInactivate(tokenId, score, atRiskDays, epoch, proof) (EVM) or verify_and_inactivate(score, at_risk_days, epoch, proof) (Solana) to mark a crown inactive. The caller pays the transaction fee.

The contract MUST verify ALL of:

  1. The submitted epoch has been committed.
  2. The submitted epoch is past its challenge window (now >= committedAt + CHALLENGE_WINDOW).
  3. The submitted epoch is within MAX_RECLAIM_STALENESS of latestEpoch.
  4. The Merkle proof verifies the canonical leaf (§6.4.5) against the epoch's root.
  5. score < AT_RISK_THRESHOLD.
  6. atRiskDays >= AT_RISK_DAYS_REQUIRED.
  7. The crown exists, is currently active, and is past its grace period.

On success, the contract MUST set the crown inactive, decrement the (symbol, chainId, tokenAddress) count, and emit CrownMarkedInactive. The slot then reopens for any caller to mint a fresh crown via the standard mint path.

6.6 Operator-Provided RPC

Operators are not required to provide on-chain reads of individual crown scores — only the per-epoch Merkle root is on-chain. Casual reads SHOULD go through the operator's public read API (§6.4.6). Smart contracts that need a crown's score MUST verify a Merkle proof in their own transaction; direct on-chain reads of score are not provided.

This is a deliberate architectural choice: per-crown on-chain storage at scale costs O(crowns × epochs) operator transactions. The Merkle commitment model costs O(chains × epochs) and provides a stronger trust model (the operator's view is cryptographically anchored and challengeable).

6.7 Constants Summary

Constant Default Description
EPOCH_DURATION 1 day Time between Merkle root commits
CHALLENGE_WINDOW 24 hours After commit, root is mutable by publisher
MAX_RECLAIM_STALENESS 2 epochs Reclaim proof must use a recent epoch; bumped from 1 so a single-epoch publisher outage cannot freeze the reclaim path
AT_RISK_THRESHOLD 60 Composite score below this counts as at-risk
AT_RISK_DAYS_REQUIRED 30 Consecutive at-risk epochs required for reclaim
GRACE_PERIOD 30 days Post-mint window during which reclaim is forbidden

7. Anti-Squatting Mechanisms

7.1 First-Claim Rule

7.2 Soulbound Transfers

Crowns cannot be transferred:

This prevents:

7.3 Heartbeat Reclaim

Inactive projects lose their registration via the Merkle reclaim path (§6.5):

7.4 Release Mechanism

Owners can voluntarily release Crowns:

function releaseCrown(uint256 tokenId) external {
    require(ownerOf(tokenId) == msg.sender, "Not owner");
    require(crowns[tokenId].dealInitiatedAt == 0, "Deal pending");

    Crown storage crown = crowns[tokenId];
    crown.releasedAt = block.timestamp;

    // Burn the NFT
    _burn(tokenId);

    emit CrownReleased(tokenId, crown.symbol, crown.chainId);
}

Note: Release is blocked while a deal is pending to protect buyer expectations. Seller must cancel the deal before releasing.

7.5 Deal Constraints

Crowns are soulbound (non-transferable), but structured deals allow ownership changes under strict constraints. These minimums are enforced at the contract level and cannot be relaxed by operators.

Constraint Minimum Enforced By
Holding period 30 days ownerSince check (resets on deal)
Announcement period 7 days dealInitiatedAt check
Deal expiration 30 days dealInitiatedAt + 30 days max
Public visibility Required DealInitiated event
Buyer acknowledgment Required Buyer signature verification
Release block During deal releaseCrown() reverts if deal pending

7.5.1 Initiate Deal

Starts the 7-day announcement period. The buyer is locked at initiation and cannot be changed.

function initiateDeal(uint256 tokenId, address buyer) external {
    require(ownerOf(tokenId) == msg.sender, "Not owner");
    require(block.timestamp >= crowns[tokenId].ownerSince + 30 days, "Holding period");
    require(crowns[tokenId].dealInitiatedAt == 0, "Deal already pending");

    crowns[tokenId].dealInitiatedAt = uint64(block.timestamp);
    crowns[tokenId].dealBuyer = buyer;

    emit DealInitiated(tokenId, msg.sender, buyer);
}

7.5.2 Cancel Deal

Seller can cancel a pending deal at any time. Expired deals can also be cleaned up via cancel.

function cancelDeal(uint256 tokenId) external {
    require(ownerOf(tokenId) == msg.sender, "Not owner");

    crowns[tokenId].dealInitiatedAt = 0;
    crowns[tokenId].dealBuyer = address(0);

    emit DealCanceled(tokenId);
}

7.5.3 Execute Deal

Executes the deal after the announcement period, transferring ownership. Requires buyer signature.

function executeDeal(uint256 tokenId, bytes calldata buyerSignature) external {
    Crown storage crown = crowns[tokenId];
    require(crown.dealInitiatedAt > 0, "No pending deal");
    require(block.timestamp >= crown.dealInitiatedAt + 7 days, "Announcement period");
    require(block.timestamp <= crown.dealInitiatedAt + 30 days, "Deal expired");

    // Verify buyer signed acknowledgment
    bytes32 hash = keccak256(abi.encodePacked(tokenId, crown.dealBuyer, crown.dealInitiatedAt));
    require(recoverSigner(hash, buyerSignature) == crown.dealBuyer, "Invalid buyer signature");

    address previousOwner = ownerOf(tokenId);
    address newOwner = crown.dealBuyer;

    // Transfer ownership (the ONLY way ownership can change)
    _transferOwnership(tokenId, newOwner);

    // Reset ownership timer (new owner must hold 30 days before their own deal)
    crown.ownerSince = uint64(block.timestamp);

    // Reset deal state
    crown.dealInitiatedAt = 0;
    crown.dealBuyer = address(0);

    emit DealExecuted(tokenId, previousOwner, newOwner);
}

7.5.4 Deal Timeline

Day 0: Crown minted (ownerSince = now)
       |
       | 30-day holding period
       |
Day 30: Can initiate deal (dealInitiatedAt = now)
       |
       | 7-day announcement period
       |
Day 37: Can execute deal (minimum)
       |
       | 23-day execution window
       |
Day 60: Deal expires if not executed

After execution, the new owner's ownerSince resets, requiring another 30-day hold before they can initiate their own deal.

7.6 Fee Structure

7.6.1 Crown Claim Fees

Fee Type Amount Notes
Base claim fee $15 USD equivalent One-time fee to mint a crown

Expired crown slots are reclaimed at the standard base claim fee — no separate reclaim pricing.

7.6.2 Fee Distribution

All crown claim fees are split between the DAO treasury and the operator that facilitated the claim:

Recipient Share Purpose
DAO treasury 20% Protocol maintenance, security, governance
Operator 80% Operator revenue for running infrastructure

7.6.3 Deal Fees

Structured deals (§7.5) incur fees on the transaction value:

Fee Rate Enforced By
Protocol tax 2% of deal value Contract (hardcoded)
Operator fee Up to 5% of deal value Operator (configurable, capped by contract)
Hard ceiling 10% total Contract (protocol tax + operator fee cannot exceed)

The protocol tax (2%) is sent to the DAO treasury. The operator fee is set by each operator within the contract-enforced cap.


8. Metadata Schemas

8.1 Crown Metadata (On-Chain)

Minimal on-chain storage with trust signals:

struct CrownMetadataV2 {
    string metadataUri;           // "ipfs://Qm..." pointing to profile JSON
    bool ownershipRenounced;      // Contract ownership renounced
    bool mintAuthorityRenounced;  // Mint authority renounced
    uint64 lockedSupply;          // Tokens locked in LP/vesting
    uint32 lastRenounceCheck;     // Timestamp of last verification
}

Update Permissions: metadataUri can be updated by the crown owner, manager, or maintainer (see §3.5).

8.2 NFT Metadata (tokenURI)

Standard ERC-721 metadata format:

{
  "name": "DOGE.arb",
  "description": "Crown for DOGE on Arbitrum",
  "image": "ipfs://Qm.../crown.png",
  "external_url": "https://example.com/DOGE.arb",
  "attributes": [
    { "trait_type": "Symbol", "value": "DOGE" },
    { "trait_type": "Chain", "value": "Arbitrum" },
    { "trait_type": "Token Address", "value": "0x..." },
    { "trait_type": "Heartbeat", "value": 85 },
    { "trait_type": "Verification Method", "value": "deployer" },
    { "trait_type": "Registered", "value": "2024-01-15" },
    { "trait_type": "Linked", "value": "true" }
  ]
}

8.3 Profile Metadata (Off-Chain)

Stored at Crown's metadataUri (IPFS). The schema is versioned via the top-level version string. The canonical version is "2.1", which includes the extensible socials[] array, the extensions namespace, and the signature envelope.

{
  "version": "2.1",
  "profile": {
    "name": "Dogecoin",
    "description": "The original meme coin",
    "logoUrl": "ipfs://Qm.../logo.png",
    "bannerUrl": "ipfs://Qm.../banner.png",
    "backgroundUrl": "ipfs://Qm.../background.jpg",
    "backgroundMode": "scaled",
    "tags": ["meme", "original", "community"]
  },
  "links": {
    "website": "https://dogecoin.com",
    "whitepaper": "ipfs://Qm.../whitepaper.pdf"
  },
  "socials": [
    { "type": "twitter", "url": "https://x.com/dogecoin", "handle": "@dogecoin", "primary": true },
    { "type": "discord", "url": "https://discord.gg/dogecoin" },
    { "type": "telegram", "url": "https://t.me/dogecoin" },
    { "type": "github", "url": "https://github.com/dogecoin" },
    { "type": "reddit", "url": "https://reddit.com/r/dogecoin" },
    { "type": "bluesky", "url": "https://bsky.app/profile/dogecoin.bsky.social" }
  ],
  "contact": {
    "email": "team@dogecoin.com",
    "ensName": "dogecoin.eth"
  },
  "audits": [
    {
      "reportUrl": "https://example.com/audit.pdf",
      "auditor": "Example Security",
      "date": "2024-01-01",
      "scope": "full"
    }
  ],
  "extensions": {},
  "signature": "0x..."
}

8.4 Socials Array (Extensible)

The socials array replaces fixed social fields for future-proofing:

{
  "socials": [
    { "type": "twitter", "url": "https://x.com/token", "handle": "@token", "primary": true },
    { "type": "telegram", "url": "https://t.me/tokenchat" },
    { "type": "discord", "url": "https://discord.gg/token" },
    { "type": "bluesky", "url": "https://bsky.app/profile/token.bsky.social" },
    { "type": "lens", "url": "https://hey.xyz/u/token" },
    { "type": "custom", "url": "https://token.forum", "name": "Community Forum" }
  ]
}

Known Types: twitter, telegram, discord, github, reddit, medium, youtube, tiktok, instagram, facebook, linkedin, bluesky, lens, threads, mastodon, custom

8.5 Shared Metadata (Linked Crowns)

When crowns are linked (same wallet + same symbol across chains), they share a single metadata CID:

interface SharedMetadata {
  wallet: Address;
  symbol: string;
  metadataCid: string;            // IPFS CID shared by all linked crowns
  crowns: CrownReference[];       // References to all linked crowns
  updatedAt: timestamp;
}

interface CrownReference {
  chainId: ChainId;
  tokenId: bigint;
  tokenAddress: Address;
}

Update Flow:

  1. User edits profile on any linked crown
  2. Upload new metadata JSON to IPFS
  3. Update shared metadata CID in database
  4. For each chain with owned crown: call setMetadataURI()

8.6 Token List Output

Implementations should provide Uniswap Token List compatible output:

{
  "name": "Open Crown Verified Tokens",
  "timestamp": "2024-06-15T12:00:00Z",
  "version": {
    "major": 1,
    "minor": 0,
    "patch": 0
  },
  "tokens": [
    {
      "chainId": 42161,
      "address": "0x...",
      "name": "Dogecoin",
      "symbol": "DOGE",
      "decimals": 18,
      "logoURI": "ipfs://Qm.../logo.png",
      "extensions": {
        "crownId": "123",
        "heartbeat": 85,
        "verificationMethod": "foundation_attestation",
        "registeredAt": "2024-01-15T00:00:00Z",
        "isLinked": true
      }
    }
  ]
}

9. API Specification

Implementations should provide these API endpoints:

GET /search?q={query}

Query Parameters:
- q: Search query (symbol, name, address)
- chainId: Filter by chain (optional)
- status: Filter by status (optional)
- limit: Results per page (default: 20, max: 100)
- offset: Pagination offset

Response:
{
  "results": [
    {
      "symbol": "DOGE",
      "chainId": "eip155:42161",
      "tokenAddress": "0x...",
      "status": "registered",
      "crown": {
        "tokenId": "123",
        "owner": "0x...",
        "heartbeat": 85,
        "verificationType": "deployer",
        "isLinked": true
      },
      "market": {
        "marketCap": "1000000000",
        "volume24h": "50000000",
        "holders": 150000
      }
    }
  ],
  "total": 150,
  "limit": 20,
  "offset": 0
}

9.2 Symbol Details

GET /symbols/{symbol}

Response:
{
  "symbol": "DOGE",
  "displayName": "Dogecoin",
  "representations": [...],
  "totalMarketCap": "15000000000",
  "linkedCount": 3,
  "organization": {...}
}

9.3 Crown Details

GET /crowns/{tokenId}

Response:
{
  "tokenId": "123",
  "symbol": "DOGE",
  "chainId": "eip155:42161",
  "tokenAddress": "0x...",
  "owner": "0x...",
  "manager": "0x...",
  "maintainer": "0x...",
  "heartbeat": 85,
  "verificationType": "deployer",
  "verificationProofHash": "0x...",
  "isLinked": true,
  "mintTimestamp": "2024-01-15T00:00:00Z",
  "metadataUri": "ipfs://Qm...",
  "metadata": {...}
}

9.4 Linked Crowns

GET /crowns/{symbol}/linked?wallet={address}

Response:
{
  "symbol": "DOGE",
  "wallet": "0x...",
  "sharedMetadataCid": "Qm...",
  "crowns": [
    { "chainId": "eip155:42161", "tokenId": "123" },
    { "chainId": "eip155:8453", "tokenId": "456" },
    { "chainId": "solana:mainnet", "tokenId": "789" }
  ]
}

9.5 Verification Status

GET /verify?chainId={chainId}&tokenAddress={address}&claimant={address}

Response:
{
  "canClaim": true,
  "methods": [
    {
      "method": "deployer",
      "available": true,
      "proof": {...}
    },
    {
      "method": "owner",
      "available": false,
      "reason": "Contract has no owner function"
    }
  ]
}

9.6 Token List

GET /tokenlist?chainId={chainId}

Response: Uniswap Token List format (see 8.6)

9.7 Organization Endpoints

GET /organizations/{domain}
POST /organizations/verify
POST /organizations/attest

10. Implementation Requirements

10.1 Contract Requirements

Requirement Mandatory Description
ERC-721 Yes Crown NFT standard
ERC-5192 Yes Soulbound (locked) transfers
Verification Yes On-chain ownership verification
Heartbeat Oracle (§6) Yes Per-epoch Merkle commitment + permissionless reclaim
Public Read API (§6.4.6) Yes Score / proof / latest-root endpoints (no auth)
Events Yes Full proof in claim events
Per-Chain Yes Independent deployment per chain

10.2 Smart Contract Events

event CrownMinted(
    uint256 indexed tokenId,
    address indexed owner,
    string symbol,
    uint64 chainId,
    address tokenAddress,
    uint8 symbolType,
    uint8 verificationType,
    bytes32 verificationProofHash,
    bytes proofData
);

event CrownReleased(
    uint256 indexed tokenId,
    address indexed previousOwner,
    string symbol,
    uint64 chainId
);

event CrownExpired(
    uint256 indexed tokenId,
    address indexed previousOwner,
    string symbol,
    uint64 chainId
);

event MetadataURIUpdated(uint256 indexed tokenId, string uri);
event MetadataCidUpdated(uint256 indexed tokenId, bytes32 cid);

// Heartbeat Oracle (§6).
event HeartbeatRootCommitted(
    uint64 indexed epoch,
    bytes32 indexed root,
    uint64 committedAt,
    address indexed publisher
);

event CrownMarkedInactive(
    uint256 indexed tokenId,
    string symbol,
    uint64 chainId,
    uint8 finalScore,
    uint64 epoch,
    address indexed reclaimer
);

event ManagerSet(
    uint256 indexed tokenId,
    address indexed previousManager,
    address indexed newManager
);

event MaintainerSet(
    uint256 indexed tokenId,
    address indexed previousMaintainer,
    address indexed newMaintainer
);

event DealInitiated(
    uint256 indexed tokenId,
    address indexed seller,
    address indexed buyer
);

event DealCanceled(
    uint256 indexed tokenId
);

event DealExecuted(
    uint256 indexed tokenId,
    address indexed seller,
    address indexed buyer
);

// Peer References (§3.4a)

event PeerAdded(
    uint256 indexed tokenId,
    uint64  indexed peerChainId,
    uint256         peerCrownId,
    uint8           kind,
    address indexed actor          // Owner or PEER_OPERATOR_ROLE holder that submitted the update
);

event PeerRemoved(
    uint256 indexed tokenId,
    uint64  indexed peerChainId,
    uint256         peerCrownId,
    address indexed actor
);

10.2.1 Peer Reference Errors (§3.4a)

Implementations must surface the following errors (or equivalent idiom on non-EVM chains):

Error When
NotPeerOperator(tokenId, caller) Caller is neither the crown owner nor a PEER_OPERATOR_ROLE holder
SelfPeerNotAllowed(tokenId) Attempted to add a peer entry pointing back at this same crown on this chain
PeerListFull(tokenId, maxPeers) Peer list already contains MAX_PEERS entries
PeerNotFound(tokenId, peerChainId, peerCrownId) removePeer called on an entry that does not exist
DuplicateChainInPeerGroup(tokenId, peerChainId, existingPeerCrownId, attemptedPeerCrownId) addPeer attempted with a different peerCrownId on a chainId already referenced in the peer list. The one-peer-per-chain rule in §3.4a admits a single crown per chain; identical re-adds remain an idempotent no-op

10.2.2 Peer Reference Role (§3.4a)

bytes32 public constant PEER_OPERATOR_ROLE = keccak256("PEER_OPERATOR_ROLE");

Holders of this role may write peer-crown references on behalf of crown owners. The role is intentionally narrow: it grants only the addPeer / addPeerBatch / removePeer authorities. It does NOT grant upgrade, mint, or transfer rights. Compromise of a PEER_OPERATOR_ROLE holder can produce incorrect peer metadata but cannot cause fund loss or corrupt ownership.

Owners may always call the peer functions directly on their own crowns regardless of role grants.

10.2.3 Peer Reference Functions (§3.4a)

Function Caller Notes
addPeer(uint256 tokenId, CrownPeer peer) Owner OR PEER_OPERATOR_ROLE Idempotent on identical (chainId, peerCrownId); reverts DuplicateChainInPeerGroup on a different peerCrownId on an already-referenced chainId (one-peer-per-chain rule, §3.4a)
addPeerBatch(uint256 tokenId, CrownPeer[] peers) Owner OR PEER_OPERATOR_ROLE Batch variant; per-entry idempotency. Reverts the whole batch on any one-peer-per-chain violation — partial-state writes would leave an ambiguous peer list
removePeer(uint256 tokenId, uint64 peerChainId, uint256 peerCrownId) Owner OR PEER_OPERATOR_ROLE Reverts with PeerNotFound if missing. Used to free a chain slot before re-pointing it to a different peerCrownId
getPeers(uint256 tokenId) returns (CrownPeer[]) View Returns the full array in storage order
peerCount(uint256 tokenId) returns (uint256) View Convenience — same as getPeers().length
MAX_PEERS() returns (uint256) View, constant Implementation-defined upper bound; the reference implementation uses 50

10.3 Data Layer Requirements

Implementations must provide a data layer that:

Implementations should use direct RPC queries to blockchain nodes rather than third-party indexing services to minimize external dependencies and maximize reliability.

10.4 Per-Chain Deployment Model

The standard uses independent crown contract deployments per chain:

+--------------+  +--------------+  +--------------+  +--------------+
|  Arbitrum    |  |    Base      |  |  Polygon     |  |   Solana     |
|   Crown      |  |   Crown      |  |   Crown      |  |   Crown      |
|  Contract    |  |  Contract    |  |  Contract    |  |  Program     |
+--------------+  +--------------+  +--------------+  +--------------+
      |               |               |               |
      +---------------+---------------+---------------+
                              |
                    +-----------------+
                    |  Shared Metadata |
                    |    Database      |
                    +-----------------+

Benefits:

Coordination:

10.5 Upgrade Path

Crown contracts should be upgradeable (e.g., UUPS) to allow:

Upgrades must not:

10.6 Symbol Handling

Implementations must not enforce arbitrary symbol format restrictions. Instead:

  1. Accept chain-native formats - Allow any symbol format the target chain supports
  2. Warn about compatibility - Display warnings when symbols limit cross-chain expansion
  3. Reference Appendix D - Use chain-specific constraints from Appendix D for validation
  4. Non-blocking warnings - Compatibility warnings should inform, not prevent claims

Implementations should warn users about cross-chain compatibility limitations. See Appendix D for chain-specific constraints and implementation guidance.


11. Security Considerations

11.1 Verification Security

11.2 Oracle Security

11.3 Upgrade Security

11.4 Economic Security


12. Governance

12.1 Standard Governance

The Open Crown Standard is governed through a progressive decentralization model defined in detail by OCP-0002 (Crown-Weighted Governance). This section summarizes the model; OCP-0002 is the normative reference for all governance mechanics, thresholds, and phase triggers.

Phase Trigger Model Crown Holder Rights
1 (Pre-Mainnet) Formation → mainnet launch Multisig with public OCP comment None binding; comment only
2 (Bootstrap) Mainnet launch ("T+0") Multisig with public OCP comment None binding; opinion polls active
2.5 (Veto Narrowing) 500 active crowns Multisig; founder veto narrows None binding; polls + scoped veto commitment
3 (Community Voice) 10,000 active crowns OR T+24 months post-mainnet (whichever is FIRST) On-chain VoteAggregator + timelock Tier 1-2 binding votes; founder veto overridable
4 (Shared Governance) 100,000 active crowns On-chain Governor Tier 3 binding votes; reactive veto live
5 (Full DAO) 1,000,000 active crowns OR December 31, 2030 (whichever is FIRST) On-chain Governor + dual-key constitutional Tier 4 dual-key; multisig migrates to elected security council

The T+24-month time anchor on Phase 3 is the protocol's commitment that the founder-ratification window has a hard end date regardless of crown count. Phase 5's December 31, 2030 backstop guarantees no individual retains permanent authority.

Voting thresholds (Tier 1-4 quorums, approvals, time-locks, override mechanisms, dual-key constitutional process) and the Bootstrap Period treasury policy are specified in OCP-0002 §2, §2a, §3a, and §10 respectively.

12.2 Multisig Model

Protocol operations during Phases 1-4 are controlled by a 2-of-3 multisig with the following responsibilities:

Operation Requirement Timelock
Contract upgrades 2-of-3 7 days
Parameter changes 2-of-3 3 days
Emergency pause 1-of-3 None
OCP ratification (Phase 1-2.5) 2-of-3 after public comment None
Tier 4 dual-key consent 2-of-3 Per OCP-0002 §2
Signer changes 2-of-3 14 days

At Phase 5 the multisig migrates to an elected security council. See OCP-0002 §2 Tier 4 and the Spec 2a step-down schedule for the full transition.

12.3 Open Crown Proposal (OCP) Process

Changes to the standard follow the OCP process (see OCP_PROCESS.md):

  1. Draft - Author submits OCP via GitLab Issue
  2. Review - 14-day minimum community review period
  3. Decision - Multisig accepts or rejects
  4. Implementation - Accepted OCPs are implemented

OCPs are categorized as:

All OCPs are stored in the ocps/ directory with an index of proposals at ocps/README.md.

12.4 Governance Scope

Decision tiers are classified per OCP-0002 §2:

Decision Type Tier (OCP-0002) Authority
Heartbeat / anti-squat parameter changes Tier 1 Crown holder vote
Fee structure, treasury allocations, subsidy ceiling Tier 2 Crown holder vote + multisig execution
Verification type additions Tier 1 Crown holder vote
Governance OCP amendments, multisig composition Tier 3 Supermajority crown holder vote
Protocol dissolution, fundamental rewrite, founder veto removal Tier 4 Dual-key (community + multisig)
Metadata schema versioning Tier 1 Crown holder vote
Dispute resolution Tier 1-2 (per case) Crown holder vote
Emergency fixes Multisig-Only Multisig with retroactive Tier 2 ratification

12.5 Coordination Layer

Operators coordinate through multiple mechanisms:

12.5.1 IPFS Manifest

A shared manifest file stored on IPFS contains:

{
  "version": "1.0.0",
  "contracts": {
    "eip155:42161": "0x...",
    "eip155:8453": "0x...",
    "solana:mainnet": "..."
  },
  "operators": [
    { "name": "Operator A", "api": "https://api.example.com" }
  ],
  "tokenListCid": "Qm...",
  "updatedAt": "2026-01-30T00:00:00Z"
}

12.5.2 On-Chain Coordinators

Each chain deployment includes a coordinator contract that stores:

12.5.3 DNS/ENS Naming

Standard naming conventions for discovery:

12.6 Standard Versioning


13. License

This specification is released under CC-BY-4.0 (Attribution 4.0 International).

Attribution 4.0 International (CC BY 4.0)

You are free to share and adapt this material for any purpose, even
commercially, under the following terms:

Attribution — You must give appropriate credit, provide a link to the
license, and indicate if changes were made.

See https://creativecommons.org/licenses/by/4.0/ for full legal text.

Anyone may implement this standard without restriction.


Appendix A: Supported Chains

Chain CAIP-2 ID Suffix Status
Arbitrum One eip155:42161 .arb Primary
Ethereum eip155:1 .eth Supported
Base eip155:8453 .base Supported
Polygon eip155:137 .polygon Supported
BSC eip155:56 .bsc Supported
Solana solana:mainnet .sol Supported

Adding New Chains

New chains can be added by:

  1. Deploying crown contract on the chain
  2. Configuring API for chain
  3. Adding chain to registry
  4. Tuning heartbeat oracle parameters (§6.7 constants)

Appendix B: Repositories

All OpenCrown repositories are hosted on GitLab:

Repository URL Description License
oc-standard gitlab.com/opencrown/oc-standard Open Crown Standard specification and OCPs CC-BY-4.0
oc-contracts gitlab.com/opencrown/oc-contracts Smart contracts (EVM and SVM) BSL 1.1
oc-sdk gitlab.com/opencrown/oc-sdk Developer SDK for integrating with OpenCrown Apache-2.0

Organization: gitlab.com/opencrown

Getting Involved

  1. Report Issues: Open an issue on the relevant repository
  2. Propose Changes: Submit an OCP via GitLab MR (see OCP_PROCESS.md)
  3. Contribute Code: Fork the repository and submit a merge request
  4. Join Discussion: Participate in issue discussions and OCP reviews

Reference Implementation


For implementation details, see:


Appendix D: Chain-Specific Constraints

Different blockchains impose different constraints on token symbols. This appendix documents known limitations to help implementations provide accurate cross-chain compatibility warnings.

D.1 Symbol Length Constraints

Chain Min Length Max Length Notes
Solana 1 10 SPL Token standard limit
EVM (all) 1 No limit Practical limit ~32 chars for display
Arbitrum 1 No limit EVM compatible
Base 1 No limit EVM compatible
Polygon 1 No limit EVM compatible
Ethereum 1 No limit EVM compatible
BSC 1 No limit EVM compatible

D.2 Character Set Constraints

Chain Allowed Characters Case Sensitivity
Solana A-Z, a-z, 0-9 Case preserved
EVM (all) Any UTF-8 Case preserved

D.3 Cross-Chain Compatibility

For maximum cross-chain compatibility, symbols should:

However, implementations don't have to enforce these as hard requirements. Instead:

D.4 Implementation Guidance

Implementations should warn users about cross-chain compatibility limitations:

Example warning for a 15-character symbol:
"This symbol exceeds 10 characters and cannot be claimed on Solana.
Your crown will be EVM-only."

Warnings should be:


Appendix E: Chain Namespaces

Chain namespaces provide implementation-specific guidance for deploying the Open Crown Standard on different blockchain ecosystems. Each namespace documents the considerations, constraints, and patterns for its target environment.

E.1 Namespace Structure

Namespaces follow the CAIP-2 identifier format:

<namespace>:<reference>

Where:

E.2 Available Namespaces

Namespace Ecosystem Documentation
eip155 EVM-compatible chains namespaces/evm/
solana Solana namespaces/solana/

E.3 Namespace Contents

Each namespace directory includes:

E.4 EVM Namespace Highlights

The eip155 namespace covers EVM-compatible chains with these key considerations:

See namespaces/evm/README.md for full details.

E.5 Solana Namespace Highlights

The solana namespace covers Solana with these key considerations:

See namespaces/solana/README.md for full details.

E.6 Adding New Namespaces

New namespaces can be proposed via the OCP process:

  1. Create namespace directory under namespaces/
  2. Document chain-specific considerations
  3. Submit OCP for community review
  4. Deploy reference implementation
  5. Update namespace index

Appendix F: Operator Resolution Guidance

This appendix provides guidance for operators on handling queries when multiple crowns exist for the same symbol on the same chain. This is operator guidance, not normative protocol specification.

F.1 Query Behavior

All canonical URL paths are symbol-first. This keeps the data model visible in the URL (peers are instances of a symbol across chains) and removes the ambiguity that arises when /chain/symbol and /symbol/chain can both route. Operators MUST return all matching crowns:

Query Type Returns Example
/symbol Default peer for symbol — operator picks (see G.4) /DOGE returns the default DOGE crown
/symbol/N Nth peer globally, by stable index (see G.3) /DOGE/1 returns the 1st DOGE peer ever minted
/symbol/chain Default peer on chain /DOGE/arb returns the default DOGE crown on Arbitrum
/symbol/chain/N Nth peer on chain, by stable per-chain index (see G.3) /DOGE/arb/1 returns the 1st DOGE crown on Arbitrum
/symbol/chain/tokenAddress Single crown (exact match by address) /DOGE/arb/0x123... returns specific crown

The canonical form is symbol-first. Operators MUST NOT emit alternate URL shapes.

F.2 Response Format

Single Crown Response:

{
  "symbol": "DOGE",
  "chainId": "eip155:42161",
  "tokenAddress": "0x123...",
  "crown": {
    "tokenId": "123",
    "owner": "0x...",
    "heartbeat": 85
  }
}

Multiple Crowns Response:

{
  "symbol": "DOGE",
  "chainId": "eip155:42161",
  "count": 3,
  "onChainCount": 3,
  "crowns": [
    {
      "tokenAddress": "0x123...",
      "tokenId": "123",
      "heartbeat": 85,
      "marketCap": "1000000000"
    },
    {
      "tokenAddress": "0x456...",
      "tokenId": "456",
      "heartbeat": 72,
      "marketCap": "500000"
    },
    {
      "tokenAddress": "0x789...",
      "tokenId": "789",
      "heartbeat": 65,
      "marketCap": "10000"
    }
  ]
}

F.3 On-Chain Verification

Users can verify operators are returning all crowns by comparing:

  1. Operator count: Number of crowns returned by operator API
  2. On-chain count: getCrownCount(symbol, chainId) from the registry contract
// Verify operator integrity
uint256 onChainCount = registry.getCrownCount("DOGE", 42161);
uint256 operatorCount = api.getCrowns("DOGE", 42161).length;
require(onChainCount == operatorCount, "Operator hiding crowns");

Mismatch Handling:

F.4 Sorting and Filtering

Operators MAY sort results by relevance metrics:

Operators MAY filter results but must clearly indicate filtering:

Operators MUST NOT:

F.5 Disambiguation UI

When multiple crowns match a query, operators should:

  1. Show count: "3 DOGE tokens found on Arbitrum"
  2. Display distinguishing info: Token address, market cap, holder count, heartbeat
  3. Provide verification: Link to on-chain count verification
  4. Allow explicit selection: Let users choose specific token by address

Appendix G: ENS/SNS Resolution Patterns

This appendix provides optional patterns for integrating crowns with blockchain naming services. This is operator guidance, not protocol mandate.

G.1 ENS Naming Pattern (EVM)

Suggested pattern for ENS subdomains. Index, when present, lives in the HTTP path rather than as a hyphenated subdomain label so the same identifier reads the same way as the canonical URL form §F.1 (/symbol/chain/N):

<symbol>.<chain>.opencrown.eth[/<index>]

Examples:
- doge.arb.opencrown.eth           (single DOGE on Arbitrum, or default)
- doge.arb.opencrown.eth/1         (first DOGE crown by registration order)
- doge.arb.opencrown.eth/2         (second DOGE crown by registration order)

Resolution Rules:

G.2 SNS Naming Pattern (Solana)

Suggested pattern for Solana Name Service (mirrors G.1 — index in path):

<symbol>.<chain>.opencrown.sol[/<index>]

Examples:
- doge.sol.opencrown.sol           (single DOGE on Solana, or default)
- doge.sol.opencrown.sol/1         (first DOGE crown)

G.3 Index Assignment

Peer indices are append-only stable. An index, once assigned to a crown, identifies that crown permanently — even after release or expiration. This lets URL shapes like /SYMBOL/CHAIN/N and ENS names like SYMBOL.CHAIN.opencrown.eth/N serve as durable references that never silently point at a different crown.

Two index scopes are defined:

Scenario Index Behavior
First crown minted Receives index 1 in both scopes
Next crown minted on the same chain Receives next per-chain index; global index also increments
Next crown minted on a different chain Receives per-chain index 1 on that chain; global index continues from last global
Crown released Index is retained by the released crown record and is NOT reused
Crown expired Index is retained; a reclaimer of the symbol on the same chain gets a NEW index, not the expired one

Indexes are append-only. Reuse would break the primary invariant that permalinks like SYMBOL.CHAIN.opencrown.eth/1 can be shared without ever silently redirecting to a different crown, and would force third-party indexers and audit trails to track operator-internal state.

Important: Index numbers are identifiers, not quality signals. They do NOT imply:

G.4 Default Resolution (Optional)

When index is omitted (bare /SYMBOL or /SYMBOL/CHAIN), operators MAY implement a default crown selection algorithm. The default SHOULD be scope-aware — bare /SYMBOL selects across all chains, bare /SYMBOL/CHAIN selects within that chain only.

Option A: Highest Heartbeat (recommended — matches the protocol's anti-squatting intent; picks the crown most engaged with by the market):

default = crowns.sortBy(heartbeat, DESC).first()

Option B: Highest Market Cap

default = crowns.sortBy(marketCap, DESC).first()

Option C: First Registered

default = crowns.sortBy(mintTimestamp, ASC).first()

Operators should document which algorithm they use. A reference operator (Chain Daddy) uses Option A for /SYMBOL and /SYMBOL/CHAIN.

G.5 Operator Implementation

Two-layer resolution. The ENS/SNS name layer maps <symbol>.<chain>.opencrown.eth to an on-chain address (the default peer's tokenAddress, by §G.4). The HTTP layer above it interprets a trailing /<index> path segment to override that default. Splitting the layers this way matches the canonical URL form §F.1 and lets the ENS resolver stay agnostic about index disambiguation:

// ENS/SNS name resolver — returns the default peer's tokenAddress.
function resolveName(name: string): Address | null {
  const parsed = parseCrownName(name);
  // parsed = { symbol: "doge", chain: "arb" }
  const crowns = getCrowns(parsed.symbol, parsed.chain);
  return crowns.sortBy('heartbeat', 'desc')[0]?.tokenAddress ?? null;
}

// HTTP layer — index in the path overrides the name-layer default.
function resolveProfileURL(host: string, path: string): Address | null {
  const parsed = parseCrownHost(host);
  // parsed = { symbol: "doge", chain: "arb" }
  const segments = path.split('/').filter(Boolean);
  const index = segments.length === 1 ? parseInt(segments[0], 10) : null;

  const crowns = getCrowns(parsed.symbol, parsed.chain);
  if (index && index >= 1) {
    return crowns.find(c => c.registrationIndex === index)?.tokenAddress ?? null;
  }
  return crowns.sortBy('heartbeat', 'desc')[0]?.tokenAddress ?? null;
}

G.6 User Guidance

When displaying crown names to users:

Wallets and applications should: