# 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.

| Field             | Type                      | Notes                                                                  | Required |
| ----------------- | ------------------------- | ---------------------------------------------------------------------- | -------- |
| `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.

```ts
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:

```ts
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:

1. Pin the JSON: `const cid = await avatar.profile.update(profileData);`
2. The helper converts the CIDv0 into the bytes32 digest (`cidV0ToHex`) and submits `nameRegistry.updateMetadataDigest`.
3. 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:

```ts
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:

```ts
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:

```ts
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, while `avatar.profile.update` handles write flows.
* **Via RPC:** `CirclesRpc.avatar.getAvatarInfo` includes the `cidV0` so 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aboutcircles.com/circles-sdk/circles-profiles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
