Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up Interoperable User Profiles Across dApps

A developer tutorial for architecting user profile schemas that can be read and updated by multiple dApps. Covers standards, data models, and implementation steps for portable, verifiable profiles.
Chainscore © 2026
introduction
GUIDE

Setting Up Interoperable User Profiles Across dApps

Learn how to implement a unified user identity that works seamlessly across multiple decentralized applications.

An interoperable dApp profile is a user identity that persists across different decentralized applications and blockchain networks. Unlike traditional web2 profiles siloed within single platforms, these profiles allow users to carry their reputation, credentials, and assets with them. This is powered by decentralized identifiers (DIDs) and verifiable credentials (VCs), which are stored in user-controlled wallets like MetaMask or WalletConnect-compatible apps. The goal is to create a seamless, user-centric web3 experience where actions on one dApp can inform interactions on another.

The technical foundation relies on open standards. A common approach uses the W3C Decentralized Identifier (DID) specification, where a DID is a unique URI (e.g., did:ethr:0xabc...) pointing to a DID Document. This document contains public keys and service endpoints for authentication. For profile data, Verifiable Credentials (JSON-LD or JWT formats) issued by trusted entities are stored and presented by the user. Protocols like Ceramic Network's ComposeDB or Lens Protocol's social graph provide data models and scalable infrastructure for storing and querying this portable profile information.

To set up a basic interoperable profile, start by integrating a DID method library. For Ethereum, this could be ethr-did or did:key. The core flow involves: 1) generating a DID for the user, 2) creating a profile data schema, and 3) writing/reading data to a decentralized storage network. Here's a simplified code snippet for creating an ethr-did:

javascript
import { EthrDID } from 'ethr-did';
const provider = // your ethers/web3 provider
const ethrDid = new EthrDID({ identifier: '0xUserAddress', provider, chainNameOrId: 'mainnet' });
console.log(ethrDid.did); // did:ethr:0xUserAddress

For storing profile attributes, you can use Ceramic's ComposeDB. Define a data model using GraphQL to structure profile fields (e.g., displayName, bio, socialLinks). Your dApp then uses the ComposeClient to perform authenticated mutations and queries against this model, with the user's DID proving ownership. This data is stored on the decentralized Ceramic network, making it retrievable by any other dApp that knows the user's DID and the model definition, enabling true cross-application interoperability.

Key considerations for developers include user privacy and data sovereignty. Profiles should be designed with selective disclosure in mind, using zero-knowledge proofs where possible. Additionally, be aware of gas costs for on-chain actions and indexing latency for off-chain solutions. Successful implementations, like Lens Protocol profiles being used across different social dApps, demonstrate the utility. The future involves broader standardization through efforts like the Decentralized Identity Foundation (DIF) to ensure profiles are portable across any blockchain or web3 ecosystem.

prerequisites
PREREQUISITES AND SETUP

Setting Up Interoperable User Profiles Across dApps

Learn the foundational concepts and technical requirements for building a portable identity layer that works across decentralized applications.

An interoperable user profile is a composable identity layer that allows users to carry their reputation, credentials, and preferences across different decentralized applications. Unlike isolated profiles tied to a single app, these profiles are built on open standards like ERC-725 for identity or ERC-1155 for multi-token assets, enabling data portability. The core prerequisite is a smart contract wallet (like Safe or ERC-4337 account abstraction) or a standard Externally Owned Account (EOA) that will serve as the profile's controller and primary identifier on-chain.

To begin development, you'll need a basic Web3 development environment. This includes Node.js (v18+), a package manager like npm or yarn, and a code editor. Essential libraries are ethers.js v6 or viem for blockchain interaction and Hardhat or Foundry for smart contract development and testing. You must also configure a connection to a blockchain network; starting with a local testnet using Hardhat Network or Anvil is recommended before deploying to public testnets like Sepolia or Holesky.

The architectural setup involves deploying two key components: a registry contract and a profile contract. The registry (often following ERC-6551 for token-bound accounts) maps a user's primary wallet to their profile contract address. The profile contract itself holds the user's data, which can include attestations from verifiable credential issuers (using EIP-712 signatures), a list of connected social accounts, or on-chain activity history. Your development environment must be configured to compile and deploy these contracts, managing dependencies like @openzeppelin/contracts for secure implementations.

For off-chain data associated with the profile, you need to decide on a storage solution. Options include decentralized storage like IPFS or Arweave, using platforms such as Lighthouse.storage or Bundlr Network, or attested on-chain storage with protocols like Ethereum Attestation Service (EAS). Your setup should include SDKs for these services, for example, the lighthouse-web3 SDK for file uploads or the @ethereum-attestation-service/eas-sdk for creating attestations that link to the user's on-chain profile.

Finally, integrate a sign-in method for users. While traditional MetaMask connection via window.ethereum works, consider WalletConnect v2 for broader wallet support or SIWE (Sign-In with Ethereum) for a standardized authentication pattern. Implement a frontend using a framework like Next.js or Vite, and use a state management library such as wagmi to handle connection state, profile data fetching, and transaction sending. This completes the initial setup for building and testing interoperable profiles.

key-concepts-text
DECENTRALIZED DATA AND SOCIAL GRAPHS

Interoperable User Profiles: A Technical Guide

Learn how to build and query portable user profiles using decentralized identity standards, enabling seamless experiences across dApps.

An interoperable user profile is a portable data set that moves with a user across different decentralized applications. Unlike traditional web2 profiles locked in siloed databases, these profiles are anchored to a user's decentralized identifier (DID) and stored on decentralized storage networks like IPFS or Arweave. This architecture shifts control from applications to users, allowing them to selectively share profile attributes—such as reputation scores, social connections, or achievement badges—with any dApp that supports the underlying standards. The core challenge is creating a common data schema and attestation framework that applications can universally understand.

The technical foundation relies on three key standards: W3C Decentralized Identifiers (DIDs) for persistent, cryptographically-verifiable identity, Verifiable Credentials (VCs) for attested claims, and the Ceramic Network's DataModel for composable data schemas. A DID, like did:key:z6Mk..., acts as the root identifier. Profile data is structured as a graph of linked documents, where each piece of information (a 'follow', a 'like', a skill credential) is a verifiable, mutable stream. For example, a user's Lens Protocol social graph or a Gitcoin Passport score can be represented as VCs issued to their DID, forming a portable reputation layer.

To implement this, developers typically use a DID method like did:pkh (for blockchain accounts) or did:key. The profile data itself is often stored using Ceramic's ComposeDB or Tableland for mutable, SQL-like data, while static assets go to IPFS. Here's a basic example of querying a profile using the Ceramic GraphQL API:

graphql
query {
  viewer {
    basicProfile {
      name
      description
      avatar
    }
    socialConnections(first: 5) {
      edges {
        node {
          did
          profile {
            name
          }
        }
      }
    }
  }
}

This query fetches a user's profile and their social graph from a shared data model.

For dApp integration, the flow involves: 1) Authentication: A user connects their wallet (e.g., via Sign-In with Ethereum), which resolves to a DID. 2) Profile Resolution: The dApp queries decentralized networks using the DID to fetch the user's profile documents. 3) Data Writing: When updating a profile, the dApp creates a signed commit to the user's data stream. Crucially, the dApp does not own the data; it merely has permission to write to it if the user signs a message. Libraries like Self.ID SDK abstract much of this complexity, providing React hooks for reading and writing profile data.

Major use cases include cross-dApp reputation, where a user's governance participation in one DAO informs their voting weight in another, and portable social graphs, as seen with Lens Protocol and Farcaster. The main challenges are data indexing performance and schema evolution. As the ecosystem matures, attestation protocols like EAS (Ethereum Attestation Service) and Verax are becoming critical for issuing and verifying trust-minimized credentials that populate these portable profiles, moving beyond self-asserted data to a web of verifiable claims.

CORE PROTOCOLS

Profile Protocol Comparison: Ceramic vs. Lens vs. ENS

A technical comparison of the data models, governance, and interoperability features of leading decentralized identity protocols.

Feature / MetricCeramic NetworkLens ProtocolEthereum Name Service (ENS)

Primary Data Model

Decentralized data streams (Ceramic Documents)

Social graph stored as NFTs

Name-to-address mapping on-chain

Underlying Storage

IPFS + Ceramic network

Polygon PoS blockchain

Ethereum mainnet

Profile Composability

High (GraphQL, cross-dApp data linking)

High (Follow/Collect/Mirror modules)

Low (Primarily for wallet addressing)

Write/Update Cost

~$0.001 per update (variable)

~$0.50 - $2.00 (Polygon gas)

~$20 - $100+ (Ethereum gas for .eth)

Decentralized Governance

Yes (Ceramic DAO)

Yes (Lens DAO)

Yes (ENS DAO)

Primary Use Case

Portable user data for any dApp

Decentralized social networking

Human-readable wallet addresses & DNS

Interoperability Standard

W3C DID, JSON-LD, GraphQL

ERC-721, EIP-712, Open Actions

ERC-137, EIP-634, CCIP-Read

Annual Renewal Fee

None (pay-per-write)

None (one-time NFT mint)

~$5 - $640+ per .eth name/year

step-1-data-model
FOUNDATION

Step 1: Define a Portable Profile Data Model

A standardized data model is the essential first step for creating user profiles that can be recognized and utilized across different decentralized applications.

An interoperable profile begins with a shared schema. Instead of each dApp inventing its own user data structure, we define a common model that applications agree to use. This model specifies the core attributes of a user profile, such as a display name, avatar URI, biography, and links to social accounts. By using a standard like ERC-725 for on-chain identity or the W3C Decentralized Identifier (DID) specification, we ensure the data structure is portable and can be resolved by any compliant application.

The data model must distinguish between verifiable claims and self-asserted data. A verifiable claim, like a proof-of-humanity attestation from Worldcoin or a KYC credential, is signed by a trusted issuer and stored as a cryptographic proof. Self-asserted data, like a bio or favorite NFT, is provided directly by the user. Structuring the model this way allows dApps to trust certain attributes without relying on a central database, using the user's wallet as the source of truth.

In practice, this model is often implemented as a JSON-LD context or a Solidity struct for on-chain storage. For example, a simplified schema for a UserProfile struct might include fields for string displayName, string avatar, Attestation[] verifications, and string[] connectedAccounts. The key is that every application reading this data knows what each field represents and how to validate it, creating a seamless experience as users move between DeFi protocols, social networks, and gaming dApps.

step-2-ceramic-implementation
DATA LAYER

Step 2: Implement the Profile with Ceramic ComposeDB

This guide details how to build a reusable, interoperable user profile using Ceramic ComposeDB, enabling a unified identity across decentralized applications.

Ceramic ComposeDB provides a decentralized, graph-based data network for storing mutable, user-owned data. Unlike static data on a blockchain, ComposeDB uses streams—versioned data structures anchored to a decentralized network. For a user profile, you create a Data Model that defines the schema (like displayName, bio, avatar) and a ComposeDB Index that allows applications to query this data across all users. This architecture ensures your profile data is portable, updatable, and can be referenced by any dApp that integrates the same model.

To begin, you must define your profile's structure using GraphQL Schema Definition Language (SDL). A basic profile model might include fields for a user's DID, a display name, a biography, and a link to an avatar image. This schema is then published to your ComposeDB node, creating a globally unique Model Stream ID. This ID is the anchor that all applications will use to read and write profile data, guaranteeing interoperability. You can explore existing models on the Ceramic Explorer.

With the model published, your application's client can interact with it. Using the ComposeDB client library, you authenticate a user via their Decentralized Identifier (DID). The client then performs mutations to create or update a profile document and queries to read profile data. The code snippet below shows a basic mutation to create a profile record tied to the authenticated user's DID.

javascript
const profile = await client.executeQuery(`
  mutation CreateProfile($input: CreateProfileInput!) {
    createProfile(input: $input) {
      document {
        id
        displayName
      }
    }
  }
`, {
  input: {
    content: {
      displayName: "Alice",
      bio: "Building on Ceramic."
    }
  }
});

A key advantage of this system is data composability. Once a user creates a profile using your model, any other dApp can read that same profile by querying the ComposeDB network with the user's DID and the Model Stream ID. The user retains full control; they can update their information in one interface, and the change is reflected everywhere. This eliminates redundant profile setup across applications and creates a seamless, user-centric experience, moving beyond isolated data silos.

For production, consider runtime model validation and relation definitions. ComposeDB supports adding validation rules (like string length) directly in your schema. You can also define relations between models, such as linking a profile to a list of owned NFTs or social connections, building a rich, interconnected graph of user data. This turns a simple profile into a foundational element for social graphs, reputation systems, and personalized dApp experiences, all built on decentralized, user-owned data.

step-3-lens-integration
INTEROPERABLE IDENTITY

Step 3: Integrate Lens Protocol Profile NFTs

Learn how to connect your application to Lens Protocol to enable portable, user-owned social profiles.

Lens Protocol Profile NFTs are the core identity primitive. Each profile is a non-fungible token (NFT) minted on the Polygon PoS network, representing a user's on-chain social identity. This NFT holds the user's handle, metadata, and acts as a root for all their social graph connections—publications, follows, and collects. By integrating these profiles, your dApp can read a user's existing social data and write new content or interactions back to their portable profile, creating a seamless cross-application experience.

To begin integration, you must first interact with the Lens Protocol smart contracts. The primary entry point is the LensHub contract. You will use its functions to fetch profile data, create posts, and follow other profiles. For reading data, you can use the contract's getProfile function or query the subgraph for more complex social graph traversals. The official Lens API and Polygonscan contract page are essential resources.

A common first integration is fetching and displaying a user's profile. Using ethers.js or viem with the LensHub ABI, you can call getProfile(profileId). The returned data includes the profile's handle, metadata URI, and owner address. The metadata URI typically points to an IPFS JSON file containing the profile's display name, bio, and picture. Here's a basic code snippet for fetching a profile:

javascript
const profileData = await lensHubContract.getProfile(1); // Fetch profile with ID 1
const metadataResponse = await fetch(`https://ipfs.io/ipfs/${profileData.metadataURI}`);
const metadata = await metadataResponse.json();
console.log(metadata.name); // Display name

For write operations like creating a post, you must request a typed signature from the user via the Lens dispatcher or their EOA wallet. The process involves constructing the post content data, calling the post function on the LensHub, and handling the signature. All publications are linked to the user's Profile NFT. It's critical to handle gas sponsorship if your dApp aims for a gasless user experience; you can use the built-in gasless relay via the Broadcast mutation in the API or set up a custom relayer.

Finally, consider the user experience for new users who don't have a Lens profile. Your dApp should guide them through the profile creation process, which involves a one-time minting transaction. You can also explore using the Profileless Publication module to allow interactions for users without a profile, though these actions won't be tied to a portable identity. By implementing these steps, you enable a foundational layer of social context and user-owned data interoperability for your application.

step-4-aggregation-query
TUTORIAL

Step 4: Build a Cross-Protocol Profile Aggregator

Learn how to create a unified user profile that aggregates data from multiple decentralized applications and protocols, enabling seamless identity and reputation portability across Web3.

A cross-protocol profile aggregator solves the fragmentation of user identity in Web3. Currently, a user's reputation, activity, and credentials are siloed within individual dApps like Uniswap, Aave, or Lens Protocol. An aggregator creates a unified, user-centric view by pulling data from these disparate sources. This enables new applications, such as undercollateralized lending based on a holistic on-chain history or personalized governance power across DAOs. The core challenge is standardizing data from different smart contract interfaces and indexing it into a coherent profile.

The technical architecture involves three key components: a data indexer, a schema resolver, and a verification layer. The indexer, often built with The Graph or Subsquid, listens for events from target protocols (e.g., Swap events on Uniswap V3, Borrow events on Aave V3). The schema resolver maps this raw, protocol-specific data to a common profile schema, like the W3C Verifiable Credentials standard or a custom JSON schema. The verification layer uses cryptographic proofs or trusted oracles to confirm the indexed data's authenticity, ensuring the aggregated profile is tamper-proof.

Here's a simplified code snippet demonstrating how an indexer might handle an event from a hypothetical governance protocol and map it to a common field:

javascript
// Handler for a GovernanceVoted event
export function handleGovernanceVoted(event: GovernanceVoted): void {
  let profileId = event.params.user.toHexString();
  let profile = UserProfile.load(profileId);
  if (!profile) {
    profile = new UserProfile(profileId);
    profile.save();
  }
  // Create a new aggregated "GovernanceAction" entity
  let action = new GovernanceAction(
    event.transaction.hash.toHexString() + "-" + event.logIndex.toString()
  );
  action.profile = profileId;
  action.protocol = "Compound Governance";
  action.actionType = "VOTE";
  action.timestamp = event.block.timestamp;
  action.save();
  // Update the profile's aggregated stats
  profile.totalGovernanceVotes = profile.totalGovernanceVotes + 1;
  profile.save();
}

This function listens for a vote, creates a standardized action record, and updates the user's aggregated vote count.

For the profile to be truly interoperable, you must design a portable data schema. This schema defines the core fields of a user's aggregated identity, such as totalDeFiVolume, governanceParticipationScore, socialGraphConnections, or heldCredentials. Using a standard like ERC-725 or ERC-735 for claim management can provide a blockchain-native storage layer. The aggregator then becomes a service that populates this portable identity with verified data from across the ecosystem. Developers can query this aggregated profile via a GraphQL API to build applications that understand the user's complete Web3 footprint, not just their activity on a single chain or app.

Key considerations for production include privacy and data freshness. Aggregating a full on-chain history can reveal sensitive financial patterns. Implement opt-in mechanisms and consider zero-knowledge proofs for private attestations. Data freshness is critical for applications like credit scoring; you'll need to design indexers that update in near real-time, not just index historical data. Furthermore, you must handle schema evolution as new protocols emerge and old ones upgrade their event structures, ensuring backward compatibility for existing user profiles.

By building a cross-protocol profile aggregator, you create the foundational data layer for a user-owned internet identity. This enables use cases from Sybil-resistant airdrops and reputation-based access to composable social graphs. The end goal is to shift the paradigm from applications owning user data to users owning and presenting a verifiable, aggregated profile of their choosing to any dApp they interact with.

DEVELOPER TROUBLESHOOTING

FAQ: Interoperable Profile Implementation

Common questions and solutions for developers building with cross-dApp user profiles using standards like ERC-6551, ENS, and Lens Protocol.

ERC-6551 TBAs are smart contract wallets. Transaction failures are often due to insufficient gas or incorrect call data. The TBA itself does not hold native tokens for gas; the signer must pay. Common issues include:

  • Insufficient signer balance: The EOA or smart account that executes executeCall must have ETH (or the chain's native token) to pay gas.
  • Wrong execution path: Calls must be made via the TBA's executeCall function, not directly to the target contract.
  • Registry vs. Account: Ensure you're interacting with the correct TBA address from the registry, not the NFT contract address.

Debugging Step: Simulate the transaction using Tenderly or a local fork first. Check the success boolean in the TransactionExecuted event.

conclusion-next-steps
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

You have now established the core components for an interoperable user profile system. This guide covered the foundational architecture, from defining a universal schema to implementing on-chain verification and off-chain data storage.

The system you've built uses a modular, multi-layered approach. The on-chain identity anchor, typically an ERC-721 or ERC-6551 token-bound account, serves as the user's persistent identifier. A standardized profile schema (like a modified EIP-725 or a custom struct) defines the core attributes. Verifiable credentials (VCs) issued by trusted attestors provide portable reputation, while decentralized storage (IPFS, Arweave, Ceramic) hosts the full profile data, referenced by the on-chain anchor. This separation ensures scalability, user control over data, and composability across the ecosystem.

For next steps, consider enhancing your implementation. Integrate with identity protocols like ENS for human-readable names or Proof of Humanity for Sybil resistance. Explore privacy-preserving techniques such as zero-knowledge proofs (ZKPs) to allow users to prove profile attributes (e.g., being over 18) without revealing the underlying data. Implement profile aggregation logic that can pull and reconcile data from multiple sources, like Lens Protocol, Galxe, and on-chain transaction history, to create a holistic view.

Finally, focus on developer adoption. Publish your profile schema and smart contract interfaces as a package on npm or as an EIP (Ethereum Improvement Proposal) to encourage standardization. Build and document clear SDKs for dApp integration, handling wallet connections, signing requests for VCs, and querying the profile graph. The true value of an interoperable profile is realized when multiple applications read from and write to it, creating a seamless, user-owned identity layer for Web3.