This EIP proposes a standard for decentralised, interoperable user profiles known as Decentralised Profiles. Profiles are implemented as Soul Bound Tokens (SBTs) that are immutable, non-transferable, and tied to unique identifiers across multiple blockchain networks. The standard provides a unified structure for user metadata, including dApp-specific customisation, default profiles, and seamless cross-chain compatibility. Profiles can be leveraged for identity management, reputation systems, and personalised dApp experiences.
Motivation
Existing solutions for decentralised identity and user profiles lack cross-chain compatibility, dApp-specific customisation, and standardisation. A unified approach is essential to:
Facilitate interoperable profiles across all chains.
Leverage the immutability and non-transferability of SBTs for secure identities.
Enable dApp-specific customisations, such as unique avatars.
Provide a robust, standards-based alternative to centralised solutions like Gravatar.
Ensure user control and decentralisation with profiles stored on IPFS/Arweave.
A common standard for Decentralised Identity that can be used across all chains.
Act as a Digital Passport for users, enabling seamless decentralised verification and authentication.
Specification
Unique Profile Identifiers
Decentralised Profile
Each profile is identified by:
<username>@<network_slug>.soul
username: User-defined, chain-unique string.
network_slug: Short identifier for the chain (e.g., eth, polygon, xion).
The profile metadata structure is designed to balance extensibility, usability, and compatibility with decentralized storage systems like IPFS. Metadata will adhere to the following schema:
{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"UserProfile","type":"object","properties":{"username":{"type":"string","description":"The unique handle of the user."},"avatar":{"type":"string","format":"uri","description":"IPFS URI pointing to the user's main avatar image."},"bio":{"type":"string","description":"Short description or biography of the user."},"website":{"type":"string","format":"uri","description":"Personal or professional website of the user."},"socials":{"type":"object","description":"User's social links.","properties":{"twitter":{"type":"string","format":"uri","description":"URL to the user's Twitter profile."},"github":{"type":"string","format":"uri","description":"URL to the user's GitHub profile."}},"required":["twitter","github"],"additionalProperties":false},"default_avatar_visibility":{"type":"string","enum":["public","private"],"description":"Default visibility setting for the main avatar."},"dapp_avatars":{"type":"object","description":"Mapping of DApp addresses to their custom avatars and visibility settings.","patternProperties":{"^0x[a-fA-F0-9]{40}$":{"type":"object","properties":{"avatar":{"type":"string","format":"uri","description":"IPFS URI for the DApp-specific avatar."},"visibility":{"type":"string","enum":["public","private"],"description":"Visibility setting for this avatar."}},"required":["avatar","visibility"],"additionalProperties":false}},"additionalProperties":false}},"required":["username","avatar","bio","website","socials","default_avatar_visibility","dapp_avatars"],"additionalProperties":false}
Here’s an example using the structure above:
{"username":"batman","avatar":"ipfs://QmExampleMainAvatarCID","bio":"Blockchain enthusiast and builder.","website":"https://anirudha.dev","socials":{"twitter":"https://twitter.com/kranirudha","github":"https://github.com/anistark"},"default_avatar_visibility":"public","dapp_avatars":{"0xDAppAddress1abcdefabcdefabcdefabcdefabcdefabcd":{"avatar":"ipfs://QmExampleAvatar1CID","visibility":"private"},"0xDAppAddress2abcdefabcdefabcdefabcdefabcdefabcd":{"avatar":"ipfs://QmExampleAvatar2CID","visibility":"public"}}}
Access Control
Default Avatar Visibility: Users can set their default avatar visibility as public or private.
dApp-Specific Avatar Visibility: Each dApp-specific avatar can also have its visibility set to public or private.
Visibility Logic:
If an avatar is public, it is retrievable by any external caller.
If an avatar is private, only the user can retrieve it. Other callers will get an encoded response alongwith error message.
dApp-Specific Avatar Customisation
Users can assign dApp-specific avatars or metadata.
If no dApp-specific customisation exists, the default avatar applies.
Rationale
The design of the Decentralised Profile Standard was guided by the need for a unified, interoperable user profile system that can operate seamlessly across all blockchain networks. Current solutions, such as ENS profiles or Gravatar, either lack cross-chain functionality, are centralised, or do not allow users to customise profiles for specific dApps. This standard addresses these shortcomings while ensuring simplicity, security, and scalability.
Design Decisions
Decentralised Identifiers (DIDs)
Using the did:<chain>:<address> format provides a globally unique identifier for profiles. This aligns with the decentralised identity movement and ensures compatibility with broader DID frameworks.
Decentralised Profile
The profile format (username@networkslug.soul) makes profiles human-readable and chain-specific while maintaining a universal structure. The .soul suffix clearly identifies profiles compliant with this standard.
dApp-Specific Avatars
Allowing users to assign dApp-specific avatars caters to personalisation and enhances the user experience. It supports scenarios where users may want different representations or metadata for different applications.
Soul Bound Tokens (SBTs)
Leveraging SBTs ensures that profiles are non-transferable, reinforcing the concept of identity ownership. SBTs prevent profiles from being sold or hijacked, making them ideal for reputation-based systems.
Registry and Resolver Architecture
This architecture was chosen for its extensibility and proven track record, as seen in ENS. It separates the management of profile identifiers (Registry) from the resolution of metadata (Resolver), making upgrades and integrations straightforward.
Compatibility with Existing Standards
The profile standard integrates with ERC-165 for interface detection and can complement ENS or other naming systems, fostering interoperability rather than competition.
Default and dApp-Specific Metadata
The inclusion of both default and dApp-specific metadata ensures flexibility. If dApp-specific metadata is not set, the default profile seamlessly applies, reducing friction for developers and users.
On-Chain Data Minimisation
Metadata is stored off-chain (e.g., IPFS or Arweave) to minimise gas costs and support scalable operations. Only URIs and pointers are stored on-chain.
Access Control
Users have complete control over avatar visibility, catering to privacy preferences.
Specific dApp-based customizations ensure fine-grained control.
dApp Identification
Requiring dApps to be identified by an address ensures traceability and security.
Extensibility
Adding metadata fields or new visibility levels does not disrupt the standard.
Profiles can remain lightweight while supporting future scalability.
Security
Access control minimizes the risk of sensitive data exposure.
Metadata stored off-chain ensures minimal gas usage and flexibility.
Alternative Designs Considered
Pure On-Chain Metadata
Storing all metadata on-chain was considered but discarded due to high gas costs, limited storage capacity, and challenges in supporting complex or large datasets such as high-resolution avatars.
Direct Integration with ENS
While integrating with ENS was explored, it was deemed limiting for cross-chain functionality, as ENS is predominantly tied to Ethereum. Decentralised profile takes inspiration from ENS while ensuring a truly multichain approach.
Fully Centralised System
A centralised system would simplify implementation but contradict the core principles of decentralisation and user sovereignty.
Non-SBT-Based Implementation
Using standard ERC721 tokens for profiles was considered but rejected since transferability is unsuitable for identity management. SBTs enforce the immutability of identity ownership.
Comparison with Related Work
ENS: Provides a robust naming service but lacks dApp-specific metadata and cross-chain functionality.
Gravatar: Centralised and cannot leverage blockchain-specific advantages like immutability, decentralisation, and SBT integration.
DID Standards: Profile must aligns with DID specifications, ensuring it fits into the broader decentralised identity ecosystem while offering features tailored to blockchain-based applications.
Scalability and Extensibility
The Registry and Resolver architecture, combined with off-chain metadata storage, ensures that profile can scale with the growth of the blockchain ecosystem. New chains, metadata types, and customisations can be added without disrupting the core functionality or introducing breaking changes.
The design balances simplicity, extensibility, and user control, making it well-suited for adoption across a wide range of dApps, wallets, and blockchains.
Contract Interface
The standard includes the following interface:
ISoulProfile
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.19;interfaceISoulProfile{structMetadata{stringusername;stringavatar;stringbio;stringwebsite;mapping(string=>string)socials;stringdefaultAvatarVisibility;mapping(address=>DappAvatar)dappAvatars;}structDappAvatar{stringavatarURI;stringvisibility;// "public" or "private"
}eventProfileCreated(addressindexeduser,stringdid,stringusername);eventAvatarUpdated(addressindexeduser,stringavatarURI,stringvisibility);eventDappAvatarUpdated(addressindexeduser,addressindexeddApp,stringavatarURI,stringvisibility);functioncreateProfile(stringcalldatausername)external;functionsetDefaultAvatar(stringcalldataavatarURI,stringcalldatavisibility)external;functionsetDappAvatar(addressdApp,stringcalldataavatarURI,stringcalldatavisibility)external;functiongetDefaultAvatar(addressuser)externalviewreturns(stringmemory,stringmemory);functiongetDappAvatar(addressuser,addressdApp)externalviewreturns(stringmemory,stringmemory);}
Reference Implementation
SoulProfile
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.19;import"@openzeppelin/contracts/access/Ownable.sol";import"@openzeppelin/contracts/utils/Strings.sol";import"./ISoulProfile.sol";contractSoulProfileisOwnable,ISoulProfile{mapping(address=>Metadata)privateprofiles;modifieronlyProfileOwner(addressuser){require(msg.sender==user,"Not authorized");_;}functioncreateProfile(stringcalldatausername)externaloverride{require(bytes(username).length>0,"Username cannot be empty");require(profiles[msg.sender].username=="","Profile already exists");profiles[msg.sender].username=username;profiles[msg.sender].defaultAvatarVisibility="public";// Default to public
emitProfileCreated(msg.sender,generateDID(msg.sender),username);}functionsetDefaultAvatar(stringcalldataavatarURI,stringcalldatavisibility)externaloverride{require(bytes(visibility).length>0,"Visibility must be set");profiles[msg.sender].avatar=avatarURI;profiles[msg.sender].defaultAvatarVisibility=visibility;emitAvatarUpdated(msg.sender,avatarURI,visibility);}functionsetDappAvatar(addressdApp,stringcalldataavatarURI,stringcalldatavisibility)externaloverride{require(dApp!=address(0),"Invalid dApp address");require(bytes(visibility).length>0,"Visibility must be set");profiles[msg.sender].dappAvatars[dApp]=DappAvatar(avatarURI,visibility);emitDappAvatarUpdated(msg.sender,dApp,avatarURI,visibility);}functiongetDefaultAvatar(addressuser)externalviewoverridereturns(stringmemoryavatarURI,stringmemoryvisibility){Metadatastorageprofile=profiles[user];return(profile.avatar,profile.defaultAvatarVisibility);}functiongetDappAvatar(addressuser,addressdApp)externalviewoverridereturns(stringmemoryavatarURI,stringmemoryvisibility){Metadatastorageprofile=profiles[user];DappAvatarstoragedappAvatar=profile.dappAvatars[dApp];return(dappAvatar.avatarURI,dappAvatar.visibility);}functiongenerateDID(addressuser)internalpurereturns(stringmemory){returnstring(abi.encodePacked("did:ethereum:",Strings.toHexString(user)));}}
Of course, this can be extended to prepare a full registry and resolver according to ENS or similar standards. Refer to Rationale above for more information about the same.
Backwards Compatibility
The Decentralised Profile system is designed to ensure smooth integration with existing decentralized applications (dApps) and platforms, while offering an upgrade path for future enhancements. Below are key considerations for backwards compatibility:
ENS Compatibility: The system adheres to the naming conventions and standards specified in EIP137 (Ethereum Name Service). This ensures that any dApps or tools already using ENS-compatible names can easily integrate profiles without additional changes.
Flexible dApp Identification: By using wallet addresses to identify dApps, the system avoids the need for centralized registration of dApps. Any existing or new dApp that interacts with Ethereum or compatible chains can use the standard by simply passing its address as a parameter.
Default Avatar Fallback: If no specific avatar is set for a dApp, the system gracefully falls back to the default avatar. This ensures that even older dApps that do not implement the latest features can continue to work seamlessly.
Upgradeable Contracts: By implementing a proxy-based architecture for all major contracts (resolver, registry, profile), the system ensures that future upgrades or changes in functionality can be deployed without disrupting existing data or workflows. Upgrades are conducted through secure proxy mechanisms like TransparentUpgradeableProxy.
Chain Agnosticism: The use of decentralized identifiers (DIDs) and chain-specific network slugs ensures interoperability across chains. This allows profiles to maintain consistency regardless of which chain they originate from, ensuring compatibility with multi-chain ecosystems.
Security Considerations
Profile system should prioritise robust security to protect user data and prevent unauthorized access.
Access Control: Users can set visibility for their avatars (public or private). This is enforced at both the resolver and registry levels to ensure unauthorized entities cannot access private avatars. Functions that modify state (e.g., setDefaultAvatar, setDappAvatar) are protected with access controls, ensuring only the profile owner can make changes.
Data Privacy: Sensitive metadata is encrypted before storage on decentralized storage systems like IPFS or Arweave. Visibility flags ensure users can control who can view specific profile elements.
Reentrancy Protection: Functions modifying state implement the checks-effects-interactions pattern or leverage ReentrancyGuard to prevent reentrancy attacks. For example, setDappAvatar ensures that all validations are performed before updating the state.
Input Validation: All user inputs (e.g., username, visibility, dApp) are validated to ensure they meet specified criteria. Invalid or malicious inputs are rejected to prevent injection attacks or other exploits. Visibility is restricted to predefined values (“public” or “private”).
Immutable DIDs: Decentralized identifiers (DIDs) for users are immutable once created. This prevents spoofing or unauthorized changes to user identities.
Fallback Mechanisms: The system provides fallback mechanisms for fetching avatars. If a dApp-specific avatar is not found, the default avatar is returned, ensuring smooth operation without errors.
Upgradeable Contracts: Proxy contracts are used to allow for upgrades while preserving state. Upgrades are performed securely via a multi-signature governance process to minimize risks.
Rate Limiting: Rate-limiting mechanisms can be implemented to prevent spam or abuse of profile creation and update functions.
Audits and Best Practices: The contracts are designed following best practices and are to be audited regularly by independent security firms. Dependencies (e.g., OpenZeppelin contracts) are reviewed and kept up to date to mitigate vulnerabilities.
dApp Address Verification: All dApps interacting with the system must be identified by a valid address. This ensures that unauthorized or spoofed entities cannot manipulate profiles or fetch restricted data.
Phishing Mitigation: User-facing dApps are encouraged to clearly display information about interactions on-chain. Users should be warned about potential phishing attacks and advised to interact only with verified dApps.
Gas Optimization: Operations are optimized to prevent gas exhaustion during execution, which could lead to incomplete transactions. This ensures that even on congested networks, the system remains functional.
Secure Fallback Functions: Fallback functions are implemented securely to prevent accidental Ether transfers or denial-of-service attacks.