{"$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}
以下是使用上述结构的示例:
{"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"}}}
// 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);}
参考实现
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";// 默认为公开
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)));}}