Wallet-Scoped Character Libraries for Realbits Apps
A paper-style architecture analysis of wallet-scoped character portability in Realbits, focusing on ERC-721 ownership reads, signature-bound sessions, metadata binding, cache invalidation, and mobile wallet integration.
Abstract
Cross-app character portability in Realbits should be treated less as a marketplace flourish and more as a distributed consistency problem. The core claim is simple: if the same wallet owns a Realbits character asset, every Realbits app should resolve that ownership to the same usable character library. The implementation is not simple, because the entitlement path crosses mobile wallet UX, signed sessions, JSON-RPC reads, ERC-721 transfer semantics, off-chain metadata, local app caches, and provider manifests.
The strongest design is a wallet-scoped character library. A wallet signature proves session control, an ERC-721 read proves present ownership, and a provider manifest binds the token to the character card, runtime preset, and model-pack compatibility. ERC-721 gives Realbits a standard read surface through functions such as ownerOf and tokenURI [S1]. Sign-In with Ethereum gives a more disciplined authentication envelope by including fields such as domain, URI, chain ID, nonce, and issued time [S2]. In the repository, this maps directly to the existing use of ethers for contract reads and signature recovery. The architectural question is therefore not whether portability is possible; it is how to make portability correct, revocable, observable, and tolerable on mobile networks.
Realbits Context
Realbits is a provider stack for local model packs, reusable character cards, and themed chat applications. The user-facing apps should behave like separate distribution surfaces, not separate entitlement systems. That distinction matters. A user who buys or mints a character through one app should not need a second account migration flow to use the same character in Realbits Chat Hub, Character Chat, or another vertical app. The common denominator is the wallet, while the product unit is the character card plus its compatible runtime assets.
The repository already contains the important primitives. packages/contracts includes the contract layer for CharacterNFT. packages/web/src/lib/character-onchain.ts reads ownerOf and tokenURI through an ethers.Contract, checks that the contract address matches the configured Realbits deployment, and rejects mismatched owners or metadata URIs. packages/web/src/lib/blockchain-service.ts uses ethers.verifyMessage to recover the signer address from a message and signature. Those pieces are not yet a complete cross-app library, but they are the correct lower-level checks.
Tactic C06 frames the product objective as cross-app character ownership. Its pending pieces are also the right ones: a shared ownership query API, wallet sign-in in each Flutter app, an owned-character surface in every app, and revocation after transfer. The technical shape should be a provider-owned read model exposed by packages/web, probably through an endpoint like /api/characters/owned-by/:address. That endpoint should not become the source of truth. It should be a cacheable projection over chain state, character metadata, and provider publishing records.
Related Work
ERC-721 is the immediate standard baseline because it makes each token individually addressable by contract address and token ID, and because it defines the ownership read that Realbits needs for revocation [S1]. OpenZeppelin's ERC-721 documentation is useful for implementation practice: it distinguishes core, metadata, enumerable, receiver, and extension contracts, and it notes that enumerable ownership tracking is often omitted because it adds overhead [S5]. That omission is important for Realbits. If CharacterNFT does not expose cheap owner enumeration, the provider API needs an indexer or database projection rather than relying on on-chain iteration from every app.
SIWE is relevant because wallet address ownership is not the same thing as application login. A user must prove control of a wallet for a particular relying party and time window, and the relying party must protect against replay. ERC-4361's message structure gives Realbits an interoperable way to bind a session to a domain, chain, nonce, and timestamp [S2]. This is a cleaner basis for mobile and web account linking than ad hoc signed strings.
Research literature adds two warnings. First, NFT ecosystems can be studied as transaction and wallet graphs, which makes ownership indexing and cross-app usage analytics feasible, but also means wallet reuse creates an observable identity graph [S8]. Second, metadata is not automatically durable just because the token is on chain. Barrington and Merrill report that a large share of sampled ERC-721 tokens failed a permanence criterion, showing that metadata quality must be measured rather than assumed [S6]. Wang, Gao, and Wei similarly find fragility in NFT-to-asset links, including inaccessible assets and duplicated assets, which directly affects character-card reliability [S7]. For Realbits, the lesson is that ownership checks must be paired with provider-controlled manifest validation.
Architecture Analysis
A Realbits portability flow has three separable layers.
The first layer is wallet-authenticated session control. On web, the current code can recover an address from a signed message. For production portability, the message should follow SIWE semantics rather than a free-form prompt. The server should issue or validate a nonce, bind the message to the Realbits domain or app origin, require the expected chain ID, and expire the session. This proves that the user controls a wallet at sign-in time. It does not prove that the user still owns a character later. That distinction prevents stale sessions from becoming permanent entitlements. SIWE also calls out privacy and key-management tradeoffs, both of which are product risks when the same wallet is reused across many services [S2].
The second layer is current ownership resolution. The provider reads ownerOf(tokenId) on the configured CharacterNFT contract and compares the returned owner to the linked wallet. It also reads tokenURI(tokenId) and compares it to the metadata URI recorded during publish or mint verification. This is close to what verifyCharacterMintOnChain already does. The ethers Contract abstraction is appropriate here because it lets the server attach an ABI to a deployed address and call view methods through a provider [S3]. The response should include the block number or observation time used for the check. Without that, mobile apps cannot reason about whether a displayed library is fresh, stale, or being refreshed.
The third layer is asset binding. The token proves ownership of a unique on-chain record; it should not carry the whole runtime payload. Realbits should keep prompts, local model requirements, voice IDs, safety policy, and app-specific presentation data in provider manifests. The token URI can point to a metadata envelope, but the app should only activate a character after the provider verifies that the URI maps to a known published character version. This preserves the repository's provider-first principle: prompts and model binaries remain off chain, while ownership remains transferable and externally inspectable.
A practical API would return a normalized list of wallet-owned character references: chain ID, contract address, token ID, character ID, manifest version, metadata URI, owner address, verified block, and revocation status. The mobile apps should cache this list locally, keyed by wallet address and manifest version. A short TTL, such as the five-minute target already mentioned in C06, is reasonable for browsing, but the chat-start path should refresh or require a sufficiently recent ownership observation. This balances responsiveness with transfer revocation.
On mobile, wallet connection is the least deterministic part of the system. Reown AppKit for Flutter documents a modal, project metadata, redirect schemes, network selection, and account buttons [S4]. That is useful for Realbits because it keeps wallet UX mostly outside the app-specific character browser. The architecture should still isolate this dependency behind a shared Flutter wallet module. Every app should emit the same wallet sign-in event, call the same provider ownership API, and render the same entitlement states: signed out, checking, owned, stale, revoked, and unsupported chain.
Limitations
The first limitation is metadata integrity. ERC-721 standardizes ownership and metadata lookup, but it cannot guarantee that external JSON, images, prompts, or model assets remain reachable. The arXiv measurements on metadata permanence and NFT-to-asset fragility should be read as a direct warning for character systems [S6][S7]. Realbits can reduce this risk by pinning immutable metadata snapshots, storing provider manifests under versioned IDs, and verifying content hashes when a character is published.
The second limitation is discovery. ownerOf(tokenId) is precise when the provider already knows candidate token IDs. It is not a wallet inventory search by itself. If the contract does not implement enumerable owner indexes, and OpenZeppelin notes that enumerable support is commonly avoided for cost reasons, Realbits needs an event indexer or database projection over Transfer events [S5]. This is not optional if the product promises an owned library without asking users to paste token IDs.
The third limitation is privacy. A wallet-scoped library intentionally correlates behavior across apps. That is the point for portability, but it creates a durable identifier. The product should let users understand that one wallet unlocks the same assets across apps, and engineering should avoid sending wallet addresses to third parties unless necessary. SIWE's privacy discussion is relevant here because address reuse is convenient but linkable [S2].
The fourth limitation is operational freshness. RPC outages, chain reorgs, delayed receipts, and mobile offline states can all create temporary disagreement between apps. The UI needs stale states, not just success and failure.
Implications for This Repository
The next repository change should be a shared ownership read model, not app-specific wallet code. packages/web is already the canonical provider surface, and it already contains the on-chain verification helper. Extending that layer into an owned-character query API keeps Realbits from duplicating entitlement rules across Flutter apps. The API should validate address format, restrict chain IDs to configured deployments, read from the contract through ethers, and return a block-tagged result [S3].
The contract package should expose enough event data for indexing. If CharacterNFT follows OpenZeppelin-style ERC-721 behavior, Transfer events can drive a provider-side projection of token ownership while direct ownerOf reads remain the final check before activation [S1][S5]. Tests should cover mint, transfer, burn or invalid token behavior, wrong chain, wrong contract, mismatched token URI, and stale cache revocation.
The Flutter app slate should consume one shared wallet and ownership module. Reown AppKit or a WalletConnect-compatible layer can provide the connection surface, but Realbits should keep the app-facing interface small: connect wallet, sign message, get address, switch network, fetch owned characters [S4]. The browser UI should place owned characters beside featured and recommended inventory while making revoked or refreshing states explicit.
Finally, telemetry should measure the architecture as a system. C06 already names wallet_sign_in, owned_character_used, and cross-app install events. Add block age, cache hit rate, ownership refresh latency, and revocation-to-hide latency. Those metrics will tell engineers whether wallet-based portability is behaving as a reliable entitlement plane or merely as a cosmetic cross-app shortcut.
References
- S1: https://eips.ethereum.org/EIPS/eip-721
- S2: https://eips.ethereum.org/EIPS/eip-4361
- S3: https://docs.ethers.org/v6/api/contract/
- S4: https://docs.reown.com/appkit/flutter/core/usage
- S5: https://docs.openzeppelin.com/contracts/5.x/api/token/erc721
- S6: https://arxiv.org/abs/2209.14517
- S7: https://arxiv.org/abs/2212.11181
- S8: https://arxiv.org/abs/2110.12545
Source Ledger
- [S1] ERC-721: Non-Fungible Token Standard (standards): https://eips.ethereum.org/EIPS/eip-721 - Defines the ownerOf, tokenURI, transfer, and metadata interfaces that Realbits can use for character ownership checks.
- [S2] ERC-4361: Sign-In with Ethereum (standards): https://eips.ethereum.org/EIPS/eip-4361 - Specifies a domain-bound, nonce-based wallet sign-in message format and discusses session and privacy considerations.
- [S3] ethers v6 Contract API (official-doc): https://docs.ethers.org/v6/api/contract/ - Documents the Contract abstraction used to call read-only smart contract methods such as ownerOf and tokenURI from JavaScript.
- [S4] Reown AppKit Flutter Usage (official-doc): https://docs.reown.com/appkit/flutter/core/usage - Documents Flutter wallet connection primitives, modal setup, redirect metadata, and account UI relevant to mobile ownership checks.
- [S5] OpenZeppelin Contracts ERC721 API (official-doc): https://docs.openzeppelin.com/contracts/5.x/api/token/erc721 - Documents practical ERC-721 implementation interfaces and cautions around enumerable metadata and transfer behavior.
- [S6] The Fungibility of Non-Fungible Tokens: A Quantitative Analysis of ERC-721 Metadata (arxiv): https://arxiv.org/abs/2209.14517 - Measures metadata permanence, immutability, and uniqueness issues that affect NFT-backed asset reliability.
- [S7] Do NFTs' Owners Really Possess their Assets? A First Look at the NFT-to-Asset Connection Fragility (arxiv): https://arxiv.org/abs/2212.11181 - Analyzes fragility in NFT-to-asset links, including inaccessible and duplicated off-chain assets, which matters for character metadata binding.
- [S8] Networks of Ethereum Non-Fungible Tokens: A graph-based analysis of the ERC-721 ecosystem (arxiv): https://arxiv.org/abs/2110.12545 - Shows how ERC-721 transactions and wallet interactions can be modeled as graphs, relevant to provider-side indexing and portability analytics.