Wallet Ownership as a Revocable Entitlement Plane for Realbits Characters
A technical analysis of cross-app character portability in Realbits as a revocable entitlement system built from wallet authentication, ERC-721 ownership reads, metadata validation, and provider-side indexing.
Abstract
Cross-app character portability in Realbits should be treated as a revocable entitlement problem, not as an asset migration problem. The user does not need each app to copy character state into its own account table. The user needs every Realbits app to answer the same question: does this authenticated wallet currently control the character asset that unlocks this card, model binding, avatar, or voice preset? ERC-721 gives Realbits a standard ownership primitive through ownerOf, transfer events, and metadata URIs [S3]. Sign-In with Ethereum gives the provider side a standard way to bind an off-chain session to the wallet that claims ownership [S4]. Ethers gives the web surface a practical read path for JSON-RPC calls and signature verification [S5].
The harder architecture work is around freshness, indexing, metadata trust, and mobile UX. Prior NFT measurement papers show that off-chain assets and metadata links can be fragile, duplicated, or unavailable at meaningful rates [S1][S2]. For Realbits, that means the NFT should authorize use of a provider-managed character record, but the runtime prompt, safety envelope, and downloadable model-pack binding should remain validated by the Realbits provider layer. Portability then becomes a small, repeatable entitlement plane shared by the flagship app, vertical Flutter apps, and packages/web.
Realbits Context
The repository already points toward this shape. The product vision defines Realbits Chat Hub as a model and app provider: web creates and publishes character inventory, while mobile and macOS apps consume reusable character cards, model packs, and themed app manifests. Tactic C06 narrows the portability hypothesis: a character owned in one Realbits app should become usable in other Realbits apps after the same wallet signs in. The active code context also includes CharacterNFT, wallet signature verification, Polygon chain selection, and verifyCharacterMintOnChain, which reads ownerOf and tokenURI from the configured contract.
That is a reasonable foundation, but the article angle matters. A previous portability article could focus on unlocking characters across apps. The useful next question is stricter: how should Realbits make ownership checks revocable, cacheable, and safe enough that every app can delegate entitlement decisions to one provider interface? The correct boundary is a shared owned-character API in packages/web, backed by on-chain reads and an indexed mirror. Mobile apps should not each learn contract ABIs, RPC fallback behavior, metadata validation, and revocation policy. They should authenticate a wallet, ask for the portable inventory for that chain-qualified account, and receive a provider-vetted list of character handles.
Related Work
ERC-721 standardizes per-token ownership. The core interface includes balanceOf, ownerOf, transfer operations, approvals, and events [S3]. This is sufficient to prove that a token ID is currently controlled by an address, but it is not by itself a complete product catalog. The base ERC-721 interface does not require an efficient query of all token IDs owned by an account. OpenZeppelin documents ERC721Enumerable as an optional extension that adds enumerability across all token IDs and per-owner token IDs [S8]. This distinction is important for Realbits: if CharacterNFT does not expose enumerable ownership, the provider must maintain an event-derived index or query an external indexer instead of looping blindly through token IDs.
SIWE complements ERC-721 by handling the off-chain identity step. ERC-4361 defines a structured message with address, domain, URI, version, chain ID, nonce, issue time, and optional expiration fields [S4]. The chain ID field matters because a raw EVM address is not globally sufficient when testnets, sidechains, and future chains share the same address format. CAIP-10 addresses the same namespace issue at the account-identifier layer by prefixing an account with a blockchain ID, which makes it suitable for internal entitlement keys such as eip155:137:0x... [S7].
The NFT literature adds a caution that standards do not solve asset integrity. Wang, Gao, and Wei studied millions of NFTs and found that off-chain asset connections can be unavailable or duplicated in practice [S1]. Barrington and Merrill similarly frame NFT value around permanence, immutability, and uniqueness, and found many ERC-721 metadata examples failing a permanence criterion [S2]. The engineering lesson is not to reject ERC-721. The lesson is to avoid pretending that a token URI alone is a durable runtime artifact. Realbits should use on-chain ownership as an entitlement input and use provider-managed manifests as the runtime source of truth.
Architecture Analysis
A portable character entitlement flow has four separate proofs. First, the app must know which wallet session it is serving. A SIWE-style challenge with nonce, domain, chain ID, and expiration gives packages/web a standard verification surface, and existing ethers usage can recover and compare signed message addresses [S4][S5]. Second, the provider must know which chain and contract are authoritative. The current Realbits code recognizes Polygon mainnet and Polygon Amoy; the official Polygon docs list chain ID 137 for Polygon PoS and 80002 for Amoy [S6]. Third, the provider must know whether a given token is currently owned by the signed account. That is the ownerOf(tokenId) read. Fourth, the provider must map the token to a vetted character record. That mapping can start from tokenURI, but it should be reconciled against the canonical provider database before being returned to apps.
The shared API should therefore expose inventory, not raw chain data. A route such as /api/characters/owned-by/:accountId can accept a CAIP-10 account ID, validate the active session, resolve the configured contracts for the chain, and return only characters that pass both ownership and provider validation. Next.js Route Handlers are a good fit because they provide custom request handlers under the app directory and are not cached by default unless explicitly configured [S9]. Ownership endpoints should probably remain dynamic at the HTTP handler level, with explicit internal caching rather than accidental framework caching.
Freshness policy is the central tradeoff. Tactic C06 mentions a five minute cache TTL. That is defensible for browsing, but not for irreversible premium actions. Realbits can split reads into two classes. The default character browser can use an indexed cache keyed by chain ID, contract address, token ID, and owner account. Starting a chat can tolerate a short TTL because mistaken access after transfer is low impact. Claiming a paid benefit, exporting a character pack, or binding creator royalties should trigger a fresh ownerOf check against the RPC provider. Ethers supports provider-connected contract calls for read-only methods, which matches this verification path without requiring user gas [S5].
Indexing strategy depends on the contract surface. If CharacterNFT adopts ERC721Enumerable, tokenOfOwnerByIndex can produce an owner inventory, but OpenZeppelin treats enumerability as a separate extension [S8]. If the contract avoids enumerable storage for gas reasons, Realbits should build an ingestion worker from transfer events. The provider can persist a table of current owner by token, last checked block, metadata URI, character ID, and revocation timestamp. The runtime API can then serve fast results from the index and selectively revalidate individual rows when TTL expires or when the app asks for a high confidence action.
Metadata should be normalized into a provider manifest. The on-chain token URI is useful as a public pointer and as an audit hook, but the Realbits runtime needs stronger guarantees: prompt schema version, SFW status, voice ID, avatar reference, model-pack compatibility, creator address, and app availability. The arXiv evidence on asset link fragility supports treating metadata availability as an operational dependency to monitor, not as an assumption [S1][S2]. On mint verification, Realbits can compare the on-chain URI to the submitted metadata. On read, the API should return the latest provider-approved manifest version and include the token linkage as provenance, not as the full execution input.
Limitations
Wallet ownership is not the same as user identity. A user can rotate wallets, use multiple wallets, lose a key, or transfer a character to another person. SIWE improves session binding, but it does not solve account recovery or social identity by itself [S4]. Realbits will still need a product-level account model if support, purchase history, or cross-device restore must survive wallet churn.
There is also a mobile signing cost. Flutter apps that rely on external wallets must handle deep links, rejected signatures, expired nonces, app switching, and inconsistent wallet support. The architecture should keep signing rare. A user signs in, receives a provider session, and then the app refreshes entitlements through ordinary API calls. Requiring wallet prompts for every character open would make portability feel like friction rather than continuity.
RPC dependency is another weak point. Polygon gives Realbits low-cost EVM compatibility, but public RPC endpoints can rate limit, lag, or disagree temporarily [S6]. The provider should use multiple RPC backends or a managed indexer for production, record the block height behind each entitlement response, and expose retryable states to clients. A response of unknown should not be treated as unowned without a reason code.
Finally, ownership can authorize access but cannot guarantee runtime safety. A transferred token may point to a character that violates current policy, depends on a removed model pack, or has stale voice assets. Provider-side manifests and release gates remain necessary.
Implications for This Repository
The immediate repository implication is to keep ownership logic centralized in packages/web. The existing character-onchain.ts pattern already validates contract address, chain ID, owner, receipt status, and token URI. That code can evolve from mint verification into a reusable entitlement service with two entry points: fresh verification for a token and indexed inventory for a wallet.
The second implication is data modeling. Realbits should store chain-qualified accounts, not lowercase addresses alone. CAIP-10 gives a compact convention for this [S7]. Character inventory rows should include chain ID, contract address, token ID, owner account ID, provider character ID, metadata URI, last verified block, and verification confidence. This makes revocation explicit and lets each app render a clear owned inventory while preserving auditability.
The third implication is client simplicity. Flutter apps should not implement ownership rules independently. They should authenticate, fetch Your Characters, and report events such as wallet_sign_in and owned_character_used. The provider can decide whether cached ownership is enough for a given action. That keeps the Realbits app slate aligned with the provider vision: apps are distribution surfaces, while the provider layer owns character inventory, entitlement policy, and manifest quality.
References
S1: https://arxiv.org/abs/2212.11181 S2: https://arxiv.org/abs/2209.14517 S3: https://eips.ethereum.org/EIPS/eip-721 S4: https://eips.ethereum.org/EIPS/eip-4361 S5: https://docs.ethers.org/v6/getting-started/ S6: https://docs.polygon.technology/pos/reference/rpc-endpoints S7: https://standards.chainagnostic.org/CAIPs/caip-10 S8: https://docs.openzeppelin.com/contracts/5.x/api/token/erc721 S9: https://nextjs.org/docs/app/getting-started/route-handlers
Source Ledger
- [S1] Do NFTs' Owners Really Possess their Assets? A First Look at the NFT-to-Asset Connection Fragility (arxiv): https://arxiv.org/abs/2212.11181 - Empirical study of NFT off-chain asset fragility, useful for separating token ownership from character metadata availability.
- [S2] The Fungibility of Non-Fungible Tokens: A Quantitative Analysis of ERC-721 Metadata (arxiv): https://arxiv.org/abs/2209.14517 - Quantitative analysis of ERC-721 metadata permanence and uniqueness, relevant to portable character-card trust assumptions.
- [S3] ERC-721: Non-Fungible Token Standard (standards): https://eips.ethereum.org/EIPS/eip-721 - Defines the ownership, transfer, balance, and metadata interface Realbits relies on for CharacterNFT checks.
- [S4] ERC-4361: Sign-In with Ethereum (standards): https://eips.ethereum.org/EIPS/eip-4361 - Defines wallet-based off-chain authentication semantics, including domain, chain ID, nonce, and session binding.
- [S5] ethers v6 Getting Started (official-doc): https://docs.ethers.org/v6/getting-started/ - Documents provider-backed read-only contract calls and message signature verification used by Realbits web wallet code.
- [S6] Polygon RPC endpoints (official-doc): https://docs.polygon.technology/pos/reference/rpc-endpoints - Official Polygon network details for PoS mainnet and Amoy, matching the chain IDs used in Realbits ownership verification.
- [S7] CAIP-10: Account ID Specification (standards): https://standards.chainagnostic.org/CAIPs/caip-10 - Defines chain-qualified account identifiers, useful for avoiding address ambiguity across Realbits apps and networks.
- [S8] OpenZeppelin Contracts ERC721 API (official-doc): https://docs.openzeppelin.com/contracts/5.x/api/token/erc721 - Documents ERC721 implementation behavior and the optional enumerable extension needed for owned-token listing tradeoffs.
- [S9] Next.js Route Handlers (official-doc): https://nextjs.org/docs/app/getting-started/route-handlers - Describes Route Handlers as custom request handlers and their caching behavior, relevant to a shared ownership query API in packages/web.