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
- Introduction
- Terminology
- Core Entities
- Verification Methods
- Trust Model
- Heartbeat Oracle
- Anti-Squatting Mechanisms
- Metadata Schemas
- API Specification
- Implementation Requirements
- Security Considerations
- Governance
- 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:
- Dogecoin (the original)
- Dozens of tokens using the DOGE ticker across different chains
- Scam tokens impersonating the original
Current solutions either:
- Rely on centralized authorities (exchanges, listing sites)
- Allow anyone to claim anything (wiki-style databases)
- Lack cross-chain identity aggregation
1.2 Solution
The Open Crown Standard provides:
- Verification-first registration - Prove ownership cryptographically before claiming
- Soulbound identity - Registrations (Crowns) cannot be transferred or sold
- Cross-chain aggregation - One Symbol entity unifies all chain representations
- Anti-squatting - Heartbeat decay removes inactive registrations
- 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:
- Index events - Read and index Crown contract events from any chain
- Serve queries - Provide API access to Crown and Symbol data
- Curate content - Flag, hide, or filter content in their UX/API (e.g., spam, scams)
- Provide UI - Build user interfaces for claiming and managing Crowns
- Cache data - Maintain local databases for performance
- Add metadata - Enrich data with off-chain information (market data, analytics)
1.4.3 Operator Restrictions
Operators MUST NOT:
- Create Crowns - Only verified token owners can mint Crowns
- Custody funds - Crown ownership remains with users at all times
- Modify on-chain state - Operators have no special contract privileges
- Bypass verification - All claims require cryptographic proof
- Alter proofs - Verification proof hashes are immutable on-chain
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:
- No single point of failure - Multiple operators can serve the protocol
- Censorship resistance - Users can switch operators freely
- Permissionless operation - Anyone can become an operator
- Data portability - All Crown data is on-chain and accessible
1.4.5 Operator Deal Requirements
When facilitating deals (ownership transfers), operators MUST:
- Display pending deals publicly - All pending deals must be visible in the operator's UX/API
- Provide communication channel - Enable communication between deal parties
- Not hide or obscure deals - Deal information must be easily discoverable
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:
- Accept any symbol format supported by the target chain
- Warn users about cross-chain compatibility limitations when claiming
- Not enforce arbitrary restrictions beyond what the target chain requires
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:
getDealState(tokenId) → DealState— see §7.5getManager(tokenId) → address— see §3.5isMaintainer(tokenId, addr) → bool— see §3.5getPeers(tokenId) → CrownPeer[]— see §3.4acommitHeartbeatRoot(epoch, root)andverifyAndInactivate(...)— see §6
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:
- No hierarchy: All linked crowns are equal peers
- Shared metadata: One IPFS CID used by all linked crowns
- Automatic: Linking happens automatically when same wallet claims same symbol
- Independent heartbeat: Each crown maintains its own heartbeat score
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:
- Advisory metadata. No fund movement depends on the peer list being correct. A stale or incomplete list degrades third-party discovery but cannot cause fund loss.
- Idempotent writes. Adding a duplicate
(chainId, peerCrownId)is a silent no-op, so operator retries converge. - One peer per chainId. A peer list may reference at most one crown per
chainId. Attempting to add a differentpeerCrownIdon a chain already represented reverts withDuplicateChainInPeerGroup(tokenId, chainId, existingPeerCrownId, attemptedPeerCrownId). The idempotent rule above still applies to identical re-adds. Rationale: the cross-chain identity model resolves a peer graph as "one sibling per chain"; admitting two siblings on the same chain would fork the graph and break downstream consumers (canonical peer designation, chain selectors, off-chain resolvers). Owners wanting to "re-point" a chain's peer mustremovePeerthe existing entry first, thenaddPeerthe new one. - Bounded size.
MAX_PEERS = 50— far above the realistic near-term chain count and caps storage/gas costs. - Self-peer is rejected.
(block.chainid, tokenId)referencing the same crown on the same registry reverts. - Same tokenId on a DIFFERENT chain is a valid peer. Token IDs are only unique within a single registry.
- Owner override. Owners can always call
addPeer/removePeerdirectly to override or bypass the operator.
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:
metadataCid— bytes32 IPFS CID (CIDv1, raw codec prefix). The resolver chain is CID → URI → null. Same delegation rules as URI.setCanonicalPeer(tokenId, chainId, peerCrownId)— designates which registered peer is canonical for cross-chain state aggregation. Owner only because canonical status is foundational, not cosmetic — unlike metadata, an erroneous canonical assignment can mislead indexers and cross-chain consumers about which deployment "represents" the crown.addPeerWithAttestation(tokenId, attestation, signer, sig)— owner produces an EIP-712 (EVM) or domain-tagged Ed25519 (Solana) signature off-chain; any relayer can submit the signed attestation. The protocol validates thatsigneris the crown holder OR the configuredPEER_OPERATOR_ROLE/peer_operatoraddress.
Delegation Rules:
- Only the crown owner can set or remove a manager
- Owner or manager can set or remove a maintainer
- Each crown has at most one manager and one maintainer
- Setting a new manager replaces the previous one (address(0) to remove)
- Setting a new maintainer replaces the previous one (address(0) to remove)
- Manager and maintainer are cleared on deal execution (crown transfer)
- Delegation does not affect heartbeat scoring or crown health
- Operator-mediated features that depend on owner-or-manager state
must verify via the same on-chain reads (
ownerOf,getManager,isMaintainer(tokenId, addr)); operators MUST NOT hold their own authoritative copy of the role hierarchy — that creates auth-bypass surfaces (operators MUST avoid this auth-bypass pattern)
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:
- Organization registers with verified domain
- Organization signs attestation message
- Attestation stored on-chain
- Crown minted with
foundation_attestationmethod
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:
- On-chain: Store only
verificationProofHash = keccak256(proof) - Events: Emit full proof in event logs
- 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:
- smooth_volume =
SMOOTH_W_24H × volume_24h + SMOOTH_W_7D_DAILY × (volume_7d / 7). Damps single-day wash-trading bursts by anchoring the score to a trailing-week daily-average. Ifvolume_7dis unavailable the formula degrades to pure 24h. - clamp_liquidity =
min(smoothed, LIQUIDITY_CAP_MULTIPLIER × liquidity_usd). A token's daily volume above this cap is treated as wash-traded — pool liquidity bounds the turnover the market can sustain organically. The cap is skipped whenliquidity_usd = 0orLIQUIDITY_CAP_MULTIPLIER = 0so newly-listed tokens with no pool data yet aren't zeroed out.
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:
- concentration_factor =
clamp(1 - top10_pct/100 - max(0, top50_pct - TOP50_ELBOW)/100, 0, 1). Penalizes top-N capture; the elbow term adds a separate penalty once the top-50 wallets exceedTOP50_ELBOW(35%) of supply. - suspicious_ratio =
(snipers + bundlers + insiders + sandwich_attackers) / total_holders. Sourced from the data provider's labeled-wallet pipeline. If labels are unavailable the ratio is treated as 0 (no penalty, no reward). - wallets_for_half = smallest
Nsuch that the top-Nwallets cumulatively own at least 50% of supply. May be derived from cumulative top-N percentages via piecewise-linear interpolation if the provider does not return it directly. - total_holders = the provider-reported holder count.
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:
- Its current composite score is
< AT_RISK_THRESHOLD(60), AND - It has been at-risk for
>= AT_RISK_DAYS_REQUIREDconsecutive epochs (default: 30), AND - It is past its post-mint
GRACE_PERIOD(default: 30 days), AND - 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:
- The submitted epoch has been committed.
- The submitted epoch is past its challenge window (
now >= committedAt + CHALLENGE_WINDOW). - The submitted epoch is within
MAX_RECLAIM_STALENESSoflatestEpoch. - The Merkle proof verifies the canonical leaf (§6.4.5) against the epoch's root.
score < AT_RISK_THRESHOLD.atRiskDays >= AT_RISK_DAYS_REQUIRED.- 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
- First verified claimant for a specific token contract wins the Crown for that contract
- Multiple crowns can exist for the same symbol on the same chain (different token contracts)
- No reservations or pre-registrations
- No admin override
7.2 Soulbound Transfers
Crowns cannot be transferred:
transferFrom()must revertsafeTransferFrom()must revertapprove()must revert
This prevents:
- Speculation on ticker names
- Secondary market trading
- Whale accumulation
7.3 Heartbeat Reclaim
Inactive projects lose their registration via the Merkle reclaim path (§6.5):
- Composite score
< AT_RISK_THRESHOLD(60) forAT_RISK_DAYS_REQUIREDconsecutive epochs (default: 30 days) - Past the post-mint
GRACE_PERIOD(30 days) - Anyone may call
verifyAndInactivatewith a Merkle proof; reclaimer pays gas - After reclaim: the (symbol, chainId, tokenAddress) slot reopens; a fresh claimant follows the standard mint flow
- Standard claim fees apply when minting into a freed slot; reclaim has no separate fee.
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:
- User edits profile on any linked crown
- Upload new metadata JSON to IPFS
- Update shared metadata CID in database
- 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:
9.1 Search
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:
- Derives Symbol entities from Crown events via direct RPC queries
- Tracks Representation status through on-chain reads and event logs
- Calculates aggregated market data from price feed APIs
- Maintains per-epoch heartbeat leaves and Merkle proof artifacts (§6.4) in a persistent store
- Tracks linked crowns via wallet+symbol association
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:
- Lower gas costs (no cross-chain messaging)
- Independent operation (no single point of failure)
- Native chain experience for users
Coordination:
- Shared metadata coordinated via off-chain database
- Wallet+symbol determines linked crowns
- Each chain's crown contract maintains an independent heartbeat oracle (§6); roots and trees do not interoperate cross-chain.
10.5 Upgrade Path
Crown contracts should be upgradeable (e.g., UUPS) to allow:
- Bug fixes
- New verification methods
- Heartbeat algorithm updates
Upgrades must not:
- Change ownership of existing Crowns
- Modify verification proofs
- Enable transfers
10.6 Symbol Handling
Implementations must not enforce arbitrary symbol format restrictions. Instead:
- Accept chain-native formats - Allow any symbol format the target chain supports
- Warn about compatibility - Display warnings when symbols limit cross-chain expansion
- Reference Appendix D - Use chain-specific constraints from Appendix D for validation
- 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
- Verify ownership at claim time, not before
- Store proof hash on-chain for immutability
- Emit full proof in events for auditability
11.2 Oracle Security
- Score inputs (volume, holders) are observed by the operator's indexer from public on-chain data; operators SHOULD cross-check against multiple data sources (e.g., Mobula + DEXScreener)
- The Merkle commitment model (§6) provides on-chain audit trail: a faulty root is challengeable by anyone running their own indexer, so single-operator data errors are detectable
- Operators SHOULD monitor publish freshness and alarm on missed epochs — a stale root delays reclaim but does not corrupt prior commits
11.3 Upgrade Security
- Timelock on contract upgrades (minimum 7 days)
- Multi-sig requirement for upgrades
- No admin functions for Crown modification
11.4 Economic Security
- Fee structure prevents spam
- First-claim-wins + heartbeat reclaim are the sole on-chain anti-squat levers (§7.1, §7.3)
- Operators MAY layer off-chain rate limits, but they are not part of the protocol
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):
- Draft - Author submits OCP via GitLab Issue
- Review - 14-day minimum community review period
- Decision - Multisig accepts or rejects
- Implementation - Accepted OCPs are implemented
OCPs are categorized as:
- Core - Contract or protocol changes
- Interface - API or indexer specifications
- Meta - Process changes
- Informational - Guidelines and best practices
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:
- Current contract addresses per chain
- Active operators and their endpoints
- Latest token list CID
- Protocol version information
{
"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:
- Current protocol version
- Manifest IPFS CID
- Emergency pause state
12.5.3 DNS/ENS Naming
Standard naming conventions for discovery:
_opencrown.example.com- DNS TXT record for operator discoveryopencrown.eth- ENS name resolving to manifest CIDregistry.opencrown.eth- ENS name for contract addresses
12.6 Standard Versioning
- Minor changes (clarifications, typographic corrections, non-normative edits): Multisig approval after 7-day public comment.
- Major changes (normative spec changes, parameter additions, semantic changes): Full OCP. Classified into the tier matching the change's scope per §12.4.
- Emergency fixes (security-critical): Multisig with retroactive Tier 2 ratification within 30 days per OCP-0002.
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:
- Deploying crown contract on the chain
- Configuring API for chain
- Adding chain to registry
- 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
- Report Issues: Open an issue on the relevant repository
- Propose Changes: Submit an OCP via GitLab MR (see
OCP_PROCESS.md) - Contribute Code: Fork the repository and submit a merge request
- Join Discussion: Participate in issue discussions and OCP reviews
Related Standards
- Chain Agnostic Improvement Proposals (CAIPs) - Cross-chain standards
- Ethereum Improvement Proposals (EIPs) - Ethereum standards
Reference Implementation
- Website: opencrown.org
- EVM Contracts: Per-chain deployments (see oc-contracts)
- Solana Program: Anchor program (coming soon)
- Data Layer: Direct RPC queries with PostgreSQL caching
Appendix C: Related Documents
For implementation details, see:
METADATA_STANDARD.md- Detailed metadata schema with extensionsTOKEN_LIST_SPEC.md- Token list export formatAPI_SPECIFICATION.md- Complete API referenceCROWN_ARCHITECTURE.md- Contract architecture and deployment
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:
- Be 10 characters or fewer (Solana limit)
- Use only alphanumeric characters (A-Z, 0-9)
However, implementations don't have to enforce these as hard requirements. Instead:
- Allow any symbol the target chain supports
- Display warnings for symbols that limit cross-chain expansion
- Let users make informed decisions about their symbol choices
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:
- Displayed prominently during claim/create flows
- Non-blocking (user can still proceed)
- Specific about which chains are affected
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:
- namespace identifies the blockchain ecosystem (e.g.,
eip155,solana) - reference identifies the specific chain (e.g.,
42161for Arbitrum)
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:
- Overview - Chain-specific implementation considerations
- Chain IDs - Supported networks in CAIP-2 format
- Contract/Program Addresses - Deployed addresses (or TBD)
- Verification Methods - Available verification types
- Gas/Compute Considerations - Cost and performance guidance
- Heartbeat Root Publisher Patterns - Per-epoch Merkle commitment infrastructure (§6)
E.4 EVM Namespace Highlights
The eip155 namespace covers EVM-compatible chains with these key considerations:
- UUPS Proxy Pattern - Upgradeable contracts with timelock governance
- Heartbeat Root Publisher - EOA holds
ROOT_PUBLISHER_ROLEand submits onecommitHeartbeatRoottx per chain per epoch (default: daily). No Chainlink Automation required. - Gas Optimization - L2 chains (Arbitrum, Base) offer ~95% cost reduction
See namespaces/evm/README.md for full details.
E.5 Solana Namespace Highlights
The solana namespace covers Solana with these key considerations:
- Program Derived Addresses (PDAs) - Deterministic Crown addresses; per-epoch
HeartbeatRootPDAs derived from epoch number - Rent Exemption - ~0.006 SOL per Crown account; ~0.0006 SOL per HeartbeatRoot account
- Anchor Framework - Recommended development framework
- Heartbeat Root Publisher - Authority designated via
set_root_publisher; submits onecommit_heartbeat_rootix per epoch (default: daily). No external scheduler required.
See namespaces/solana/README.md for full details.
E.6 Adding New Namespaces
New namespaces can be proposed via the OCP process:
- Create namespace directory under
namespaces/ - Document chain-specific considerations
- Submit OCP for community review
- Deploy reference implementation
- 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:
- Operator count: Number of crowns returned by operator API
- 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:
- If
operatorCount < onChainCount: Operator may be filtering or hiding crowns - Users should query multiple operators or use direct RPC if concerned
F.4 Sorting and Filtering
Operators MAY sort results by relevance metrics:
- Market cap (highest first)
- Trading volume (highest first)
- Heartbeat score (highest first)
- Holder count (highest first)
Operators MAY filter results but must clearly indicate filtering:
- Spam/scam flagging (with appeal process)
- Minimum heartbeat threshold (display warning)
- User preferences (saved filters)
Operators MUST NOT:
- Hide crowns without disclosure
- Claim fewer crowns exist than on-chain count
- Selectively show crowns based on payment/partnerships
F.5 Disambiguation UI
When multiple crowns match a query, operators should:
- Show count: "3 DOGE tokens found on Arbitrum"
- Display distinguishing info: Token address, market cap, holder count, heartbeat
- Provide verification: Link to on-chain count verification
- 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:
- Omitted index: Operator chooses default (typically highest heartbeat or market cap)
- Explicit index: Returns specific crown by registration order
- Invalid index: Returns error or null
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:
- Global peer index (symbol-scoped): 1-based position across all
minted peers for the symbol, ordered by
mint_timestampASCENDING withchain_idas a deterministic tiebreaker. Used in the/SYMBOL/NURL form. - Per-chain peer index (symbol+chain-scoped): 1-based position
within a single chain for the symbol, ordered by
mint_timestampASCENDING. Used in the/SYMBOL/CHAIN/NURL form and in ENS names.
| 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:
- Priority or canonical status beyond mint order
- Higher trust or verification
- Better market position
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:
- Single crown: Display as
DOGE.arb(no index needed) - Multiple crowns: Display as
DOGE.arb (3 tokens)with disambiguation - Explicit reference: Allow
DOGE.arb/1syntax for a specific crown (matches the URL path form).
Wallets and applications should:
- Show full token address when ambiguous
- Warn users when multiple crowns exist
- Provide easy access to crown details for verification