Guides

Set Profile Metadata

🚧

This request is protected by authentication

hint: this means it requires an x-access-token header put in the request with your authentication token.

📘

Lens Profile Manager Compatible: Gasless & Signless

This action can be used through the Lens Profile Manager to enable a gasless and signless experience.

Configure the metadata associated to a profile.

All the metadata of the profile is stored on-chain in a URL/IPFS link that points to the metadata just how NFTs work. This means updating profile details is a transaction.

There are two different approaches you can use to set a profile's metadata. Depending on the whitelist status of your app and the settings of the profile you would like to set metadata for, you can use:

  • a) Set Profile Metadata via Lens Profile Manager (Gasless & Signless)
  • b) Set Profile Metadata Using TypedData and Broadcasting Onchain via the API (Gasless & Signed)

To decide which approach to use, you can use the following table. Please note that a profile may also have another Profile Manager enabled that is not powered by the Lens API, this is not covered below. To learn more about checking a profile's Profile Manager settings, see Profile Manager.

Is Your App Whitelisted?Profile has Lens Profiles Manager Enabled?Approach To Use
1truetrueLens Profile Manager
2truefalseTypedData & Broadcast via API
3falsetrueTypedData & Self-Funded
4falsefalseTypedData & Self-Funded

a) Set Profile Metadata via Lens Profile Manager

If possible, using the Lens Profile Manager is the best way to set a profile's metadata. This will be a gasles and signless operation.

Request

  • metadataURI: URI (required)
    • The URL/IPFS link to the metadata file.

Invocation

const typedDataResult = await lensClient.profile.setProfileMetadata({
  metadataURI: "metadata-uri",
})
mutation SetProfileMetadata {
  result: setProfileMetadata(
    request: { metadataURI: "https://example.com/metadata.json" }
  ) {
    ... on RelaySuccess {
      ...RelaySuccess
    }
    ... on LensProfileManagerRelayError {
      ...LensProfileManagerRelayError
    }
  }
}

Response

{
  "txHash": "0x000000",
  "txId": "0x01"
}
{
  "reason": "FAILED" // LensProfileManagerRelayErrorReasonType
}

b) Set Profile Metadata Using TypedData and Broadcasting Onchain via the API

Request

  • metadataURI: URI (required)
    • The URL/IPFS link to the metadata file.

Invocation

const result =
  await lensClient.profile.createOnChainSetProfileMetadataTypedData({
    metadataURI: "metadata-uri",
  })
mutation CreateOnchainSetProfileMetadataTypedData {
  result: createOnchainSetProfileMetadataTypedData(
    request: { metadataURI: "urlToMetadataFile" }
  ) {
    ...CreateOnchainSetProfileMetadataBroadcastItemResult
  }
}

Response

{
  "id": "0x01",
  "expiresAt": "2023-10-01T00:00:00Z",
  "typedData": {
    "types": {
      "SetProfileMetadataURI": [
        { "name": "...", "type": "..." },
        { "name": "...", "type": "..." }
      ]
    },
    "domain": {
      "name": "...",
      "chainId": "...",
      "version": "...",
      "verifyingContract": "0x0000000"
    },
    "value": {
      "nonce": "...",
      "deadline": "2023-10-01T01:00:00Z"
    }
  }
}

Broadcasting the TypedData

Once you have the typed data for the follow action, you need to get the user to sign with their wallet and then broadcast it onchain.

See Broadcasting Onchain for more information.


Calling the Contract Directly

If you opt to bypass the API and directly push transactions from the client to the blockchain, you'll be responsible for encoding and validation.

This approach isn't covered in the API documentation, you can find guidance in the contract documentation.

Aligned with best practices demonstrated by projects like seaport on OpenSea, this is aimed at enhancing the user's awareness of what they're signing.


Full LensClient Example

Using the Lens Profile Manager:

// your LensClient instance must be authenticated

const result = await lensClient.profile.setProfileMetadata({
  metadataURI: "metadata-uri",
})

const data = result.unwrap()

if (!isSuccessfulLensProfileManagerResponse(data)) {
  console.log(`Something went wrong`, data)
  return
}

await lensClient.transaction.waitUntilComplete({ txId: data.txId })

Using TypedData:

// your LensClient instance must be authenticated

const typedDataResult =
  await lensClient.profile.createOnChainSetProfileMetadataTypedData({
    metadataURI: "your-metadata-uri",
  })

// typedDataResult is a Result object
const data = typedDataResult.unwrap()

// sign with the wallet
const signedTypedData = await wallet._signTypedData(
  data.typedData.domain,
  data.typedData.types,
  data.typedData.value
)

// broadcast
const broadcastResult = await lensClient.transaction.broadcastOnchain({
  id: data.id,
  signature: signedTypedData,
})

// broadcastResult is a Result object
const broadcastResultValue = broadcastResult.unwrap()

if (!isRelaySuccess(broadcastResultValue)) {
  console.log(`Something went wrong`, broadcastResultValue)
  return
}

// or wait till transaction is indexed
await lensClient.transaction.waitUntilComplete({
  txId: broadcastResultValue.txId,
})

console.log(
  `Transaction was successfully broadcasted with txId ${broadcastResultValue.txId}`
)

Profile Metadata Structure

export enum MetadataDisplayType {
  number = "number",
  string = "string",
  date = "date",
}

export enum MetadataVersions {
  one = "1.0.0",
}

export interface AttributeData {
  displayType?: MetadataDisplayType
  traitType?: string
  value: string
  key: string
}

export interface ProfileMetadata {
  /**
   * The metadata version.
   */
  version: MetadataVersions

  /**
   * The metadata id can be anything but if your uploading to ipfs
   * you will want it to be random.. using uuid could be an option!
   */
  metadata_id: string

  /**
   * The display name for the profile
   */
  name: string | null

  /**
   * The bio for the profile
   */
  bio: string | null

  /**
   * Cover picture
   */
  cover_picture: string | null

  /**
   * Any custom attributes can be added here to save state for a profile
   */
  attributes: AttributeData[]
}

Anything you put in attributes will be extracted out of the metadata and put into the profile schema under attributes.


Full GraphQL API Example

📘

Set Profile Metadata: GraphQL API Full Example