Profiles
Coming Soon — The ChainSocial SDK is not yet published. This documentation describes the planned API. Use the REST API for current integrations.
User profiles are the foundation of ChainSocial. Every user needs a profile before they can post.
Why Profiles Exist
Profiles serve multiple purposes:
- Identity — Username, display name, bio, avatar
- Discovery — Other users can find and follow you
- Gate for actions — You need a profile to post, follow, or message
Creating a Profile
Users need a username (as bytes32) to create a profile. Use the stringToHex utility from viem.
import { stringToHex, padHex } from 'viem'
// Convert username to bytes32
const username = padHex(stringToHex('alice'), { size: 32 })
// Create the profile
const result = await client.profile.createProfile(username)
console.log('Profile created:', result.hash)Username Rules
- Usernames are unique across the protocol
- Must be lowercase alphanumeric (a-z, 0-9)
- 3-20 characters
- Cannot be reserved names
Profile Fields
Profile metadata is stored as content references. The profile system uses a flexible key-value structure.
Standard fields:
| Field Key | Description |
|---|---|
displayName | Human-readable name |
bio | User biography |
avatar | Profile picture |
coverImage | Banner image |
website | Personal website URL |
location | Geographic location |
Setting a Field
import { stringToHex, padHex } from 'viem'
// First, create content for the field value
const bioContentId = await client.content.createInlineContent('Web3 developer and coffee enthusiast')
// Then set the profile field
const fieldKey = padHex(stringToHex('bio'), { size: 32 })
await client.profile.setProfileField(fieldKey, bioContentId)Setting Multiple Fields (Batch)
More gas-efficient when updating multiple fields:
// Create content for each field
const displayNameContent = await client.content.createInlineContent('Alice')
const bioContent = await client.content.createInlineContent('Building cool stuff')
// Batch update
const fieldKeys = [
padHex(stringToHex('displayName'), { size: 32 }),
padHex(stringToHex('bio'), { size: 32 }),
]
const contentIds = [displayNameContent, bioContent]
await client.profile.setProfileFieldBatch(fieldKeys, contentIds)Removing a Field
const fieldKey = padHex(stringToHex('location'), { size: 32 })
await client.profile.removeProfileField(fieldKey)Reading Profile Data
From the SDK
const fieldKey = padHex(stringToHex('bio'), { size: 32 })
const contentId = await client.profile.getProfileField(userAddress, fieldKey)
// The contentId points to content storage
// You'll need to resolve it to get the actual textFrom the API (Recommended for Reads)
The API returns fully-resolved profile data:
const response = await fetch(`https://api.chainsocial.cc/api/profiles/${address}`)
const data = await response.json()
console.log(data.profile.username) // 'alice'
console.log(data.profile.metadata?.bio) // 'Web3 developer...'
console.log(data.profile.metadata?.avatar?.uri) // 'ipfs://...'
console.log(data.followerCount) // 42API response structure:
{
"profile": {
"address": "0x...",
"username": "alice",
"joinedAt": 1704067200,
"lastActiveAt": 1704153600,
"isVerified": false,
"isActive": true,
"metadata": {
"displayName": "Alice",
"bio": "Web3 developer",
"avatar": {
"uri": "ipfs://bafkrei...",
"mimeType": "image/png"
},
"website": "https://alice.xyz"
}
},
"followerCount": 42,
"followingCount": 18
}Profile Metadata Schema
ChainSocial uses a standardized metadata format (CSPM-1):
{
"$schema": "https://chainsocial.cc/schemas/profile-metadata-v1.json",
"version": "1.0",
"displayName": "Alice",
"bio": "Building on-chain social",
"avatar": {
"uri": "ipfs://bafkrei...",
"mimeType": "image/png"
},
"coverImage": {
"uri": "ipfs://bafkrei...",
"mimeType": "image/jpeg"
},
"website": "https://alice.xyz",
"location": "San Francisco",
"links": [
{
"type": "twitter",
"url": "https://twitter.com/alice",
"handle": "@alice"
},
{
"type": "github",
"url": "https://github.com/alice"
}
]
}Using ProfileMetadataBuilder
The SDK includes a builder for constructing valid metadata:
import { ProfileMetadataBuilder } from '@chainsocial/client'
const metadata = new ProfileMetadataBuilder()
.setDisplayName('Alice')
.setBio('Web3 developer')
.setAvatar('ipfs://bafkrei...', 'image/png')
.setWebsite('https://alice.xyz')
.addLink({ type: 'twitter', url: 'https://twitter.com/alice', handle: '@alice' })
.build()
// Store as JSON in content system
const metadataJson = JSON.stringify(metadata)
const contentId = await client.content.createInlineContent(metadataJson)Updating Avatar/Cover Images
Images should be uploaded to storage first, then referenced:
// 1. Upload image (example with your storage solution)
const imageFile = await fetch('/path/to/avatar.png').then(r => r.blob())
const uploadResult = await uploadToStorage(imageFile) // Your upload function
// 2. Build metadata with IPFS URI
const metadata = new ProfileMetadataBuilder()
.setAvatar(`ipfs://${uploadResult.cid}`, 'image/png')
.build()
// 3. Store metadata and update profile
const contentId = await client.content.createInlineContent(JSON.stringify(metadata))
const fieldKey = padHex(stringToHex('metadata'), { size: 32 })
await client.profile.setProfileField(fieldKey, contentId)Deactivating a Profile
Users can deactivate their profile (soft delete):
await client.profile.deactivateProfile()Deactivated profiles:
- Still exist on-chain
- Won’t appear in feeds/search
- Can be reactivated
Activity Tracking
Update your last-active timestamp (useful for “online” indicators):
await client.profile.updateLastActive()Delegation: On-Behalf-Of
If you have delegation permissions, you can update someone else’s profile:
// Acting as a delegate for another user
await client.profile.setProfileFieldOnBehalfOf(
userAddress, // The profile owner
fieldKey,
contentId
)See the Advanced section for delegation setup.
Common Patterns
Check if User Has Profile
async function hasProfile(address: string): Promise<boolean> {
const response = await fetch(`https://api.chainsocial.cc/api/profiles/${address}`)
return response.ok
}Create Profile if Not Exists
async function ensureProfile(client, username: string) {
const address = client.walletClient.account.address
const response = await fetch(`https://api.chainsocial.cc/api/profiles/${address}`)
if (!response.ok) {
const usernameBytes = padHex(stringToHex(username), { size: 32 })
await client.profile.createProfile(usernameBytes)
}
}Next Steps
- Posts — Start posting content
- Social Graph — Build follower relationships