Setting Circles Profiles
Circles avatars are ERC‑1155 tokens whose metadata field points to an IPFS CID. The JSON stored at that CID contains the human-friendly profile (name, description, images, etc.) that explorers and UIs render. @aboutcircles/sdk exposes helpers on every avatar instance, while @aboutcircles/sdk-profiles provides you a lightweight client for direct pin/get calls.
Profile Data Model
The profile payload mirrors the Profile interface from @aboutcircles/sdk-types, and the Circles profile service enforces the same shape when accepting uploads.
name
string
Display name for the avatar or group.
✅
description
string
Free-form text for bios or summaries.
previewImageUrl
string (uri)
Must be a base64 data URL that matches the preview requirements below.
imageUrl
string (uri)
Full-size image (external URL or data URL).
location
string
Human-readable location such as "Berlin, Germany".
geoLocation
[number, number]
Tuple of [latitude, longitude].
extensions
Record<string, unknown>
Add your own structured metadata without waiting for schema changes.
For groups, GroupProfile extends Profile with a required symbol property so downstream tools can display the token ticker alongside other metadata.
import type { Profile, GroupProfile } from '@aboutcircles/sdk-types';
const humanProfile: Profile = {
name: 'John Doe',
description: 'Web3 Developer',
imageUrl: 'https://example.com/image.jpg',
previewImageUrl: 'data:image/jpeg;base64,...',
location: 'Berlin, Germany',
geoLocation: [52.52, 13.4050],
extensions: { twitter: '@john' },
};
const groupProfile: GroupProfile = {
name: 'My Community Group',
symbol: 'MCG',//REQUIRED
description: 'A community group for local initiatives',
imageUrl: 'https://example.com/group-image.jpg',
};Creating & Pinning Profiles
The SDK automatically creates a Profiles client using the profileServiceUrl from circlesConfig. You can call it directly when building forms or admin panels:
import { Sdk } from '@aboutcircles/sdk';
import { circlesConfig } from '@aboutcircles/sdk-core';
const runner = /* your ContractRunner */;
await runner.init();
const sdk = new Sdk(circlesConfig[100], runner);
const profile = {
name: 'John Doe',
description: 'Web3 Developer',
previewImageUrl: 'data:image/jpeg;base64,...',
};
const profileCid = await sdk.profiles.create(profile);
console.log('Pinned profile CID:', profileCid);If you already know the CID (for example, you pinned it off-chain), you can fetch it later via await sdk.profiles.get(cid) or through any avatar instance using await avatar.profile.get().
Updating On-Chain Metadata
For avatars you control:
Pin the JSON:
const cid = await avatar.profile.update(profileData);The helper converts the CIDv0 into the bytes32 digest (
cidV0ToHex) and submitsnameRegistry.updateMetadataDigest.Subsequent reads pull the updated profile, and you can register short names via
avatar.profile.registerShortName(nonce)if desired.
You can also skip the pinning step when you already have a CID:
await avatar.profile.updateMetadata('QmExampleCidV0');Group Profiles and Registration
Group metadata uses the same pinning flow, but the registration step also needs the group symbol and deployment parameters for the BaseGroup factory. The SDK’s register.asGroup helper wraps all of that:
import type { GroupProfile } from '@aboutcircles/sdk-types';
const groupProfile: GroupProfile = {
name: 'My Community Group',
symbol: 'MCG',
description: 'A community group for local initiatives',
imageUrl: 'https://example.com/group-image.jpg',
};
const owner = runner.address!;
const service = circlesConfig[100].coreMembersGroupDeployer;
const feeCollection = circlesConfig[100].standardTreasury;
const initialConditions: Address[] = [];
const groupAvatar = await sdk.register.asGroup(
owner,
service,
feeCollection,
initialConditions,
groupProfile.name,
groupProfile.symbol,
groupProfile
);register.asGroup pins the groupProfile, deploys the BaseGroup via core.baseGroupFactory, extracts the new group address from the BaseGroupCreated event, and returns a BaseGroupAvatar wired to your runner.
Preview Image Requirements
When you embed an image as previewImageUrl, the profile service enforces a few constraints to keep responses lightweight:
Format: PNG, JPEG, or GIF.
Dimensions: exactly 256×256 pixels.
File size: ≤ 150 KB after compression.
Encoding: Base64-encoded data URL (e.g.,
data:image/png;base64,...).
Here’s a browser-friendly helper that crops and compresses user uploads before pinning:
const preparePreview = (file: File) =>
new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const img = new Image();
img.src = reader.result as string;
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const size = 256;
if (!ctx) return reject(new Error('Canvas context unavailable'));
canvas.width = canvas.height = size;
ctx.drawImage(img, 0, 0, size, size);
const dataUrl = canvas.toDataURL('image/jpeg', 0.5);
if (dataUrl.length > 150 * 1024) {
console.warn('Preview exceeds 150 KB after compression');
}
resolve(dataUrl);
};
img.onerror = reject;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});Profiles that violate those constraints are rejected by the pinning service, so validate on the client and surface helpful errors to your users.
Reading Profiles Back
Via avatars:
await avatar.profile.get()pulls and caches the current metadata, whileavatar.profile.updatehandles write flows.Via RPC:
CirclesRpc.avatar.getAvatarInfoincludes thecidV0so you can fetch profile blobs without instantiating an avatar class.Via the profiles client:
await sdk.profiles.get(cid)is ideal for batch jobs or server-side verification.
Because the CID is stored on-chain, any tool that knows the avatar address can look up its metadata by combining RPC data with the profile service.
Last updated
Was this helpful?