Help & Support

Create an Account

This guide will help you create your first Lens account.

The process of creating a Lens Account differs slightly depending on whether you want to create a free username (such as one in the lens/ Global Namespace) or a username within a Custom Namespace that may be token-gated or require fees.

Account With Free Username

To create a new Account with a free Username, follow these steps

1

Log In to Lens

First, authenticate as an Onboarding User.

Use the @lens-protocol/client package to authenticate with the user's wallet.

import { evmAddress } from "@lens-protocol/client";import { signMessageWith } from "@lens-protocol/client/viem";import { client } from "./client";
const authenticated = await client.login({  onboardingUser: {    app: evmAddress("<your-app-address>"),    wallet: signer.address,  },  signMessage: signMessageWith(walletClient),});
if (authenticated.isErr()) {  return console.error(authenticated.error);}
// SessionClient: { ... }const sessionClient = authenticated.value;

The process is explained in detail in the Authentication guide, so we will keep it brief here.

2

Verify Username

Then, verify if the desired username is available.

Use the canCreateUsername action as follows:

import { canCreateUsername } from "@lens-protocol/client/actions";
import { client } from "./client";
const result = await canCreateUsername(sessionClient, {  localName: "wagmi",});
if (result.isErr()) {  return console.error(result.error);}
result.value; // CanCreateUsernameResult

The CanCreateUsernameResult tells you if the logged-in user satisfy the Namespace Rules for creating a username, and if the desired username is available.

Check CanCreateUsernameResult
switch (data.__typename) {  case "NamespaceOperationValidationPassed":    // Creating a username is allowed    break;
  case "NamespaceOperationValidationFailed":    // Creating a username is not allowed    console.log(data.reason);    break;
  case "NamespaceOperationValidationUnknown":    // Validation outcome is unknown    break;
  case "UsernameTaken":    // The desired username is not available    break;}

Where:

  • NamespaceOperationValidationPassed: The logged-in user can create a username under the desired Namespace.

  • NamespaceOperationValidationFailed: Reposting is not allowed. The reason field explains why, and unsatisfiedRules lists the unmet requirements.

  • NamespaceOperationValidationUnknown: The Namespace has one or more unknown rules requiring ad-hoc verification. The extraChecksRequired field provides the addresses and configurations of these rules.

  • UsernameTaken: The desired username is not available.

Treat the NamespaceOperationValidationUnknown as failed unless you intend to support the specific rules. See Namespace Rules for more information.

3

Create Account Metadata

Then, create an Account Metadata object.

The following example demonstrates the most efficient way to create Account Metadata using Grove storage.

Let's assume there is a form where the user can enter their name, bio, and a profile picture. See other Account metadata fields at the bottom of this page.

Example Form
<form onSubmit={onSubmit} id="account-form">  <label htmlFor="picture">Picture:</label>  <input type="file" name="picture" accept="image/png" multiple />
  <label htmlFor="name">Name:</label>  <input type="text" name="name" />
  <label htmlFor="bio">Bio:</label>  <textarea name="bio" rows={3}></textarea>
  <button type="submit">Sign Up</button></form>

Use an instance of the StorageClient to create a Grove folder containing your media files (such as a profile picture), and set the Account Metadata object as the index file.

import type { Resource } from "@lens-chain/storage-client";import { account } from "@lens-protocol/metadata";import { storageClient } from "./storage";
// …
async function onSubmit(event: SubmitEvent) {  event.preventDefault();  const form = event.target as HTMLFormElement;
  const name = form.elements.namedItem("name")?.value || "";  const bio = form.elements.namedItem("bio")?.value || "";  const input = form.elements.namedItem("picture") as HTMLInputElement;
  // …
  const { folder, files } = await storage.uploadFolder(input.files, {    index: (resources: Resource[]) =>      account({        name,        picture: resources[0].uri, // this is a resolved lens://… URI        bio,      }),  });
  // folder.uri will be something like `lens://4f91ca…`}

4

Deploy Account Contract

Then, use the createAccountWithUsername action to deploy Lens Account smart contract and contextually create a username for it.

import { uri } from "@lens-protocol/client";import { createAccountWithUsername } from "@lens-protocol/client/actions";
const result = await createAccountWithUsername(sessionClient, {  username: { localName: "wagmi" },  metadataUri: uri("<folder.uri>"),});

And, handle the result using the adapter for the library of your choice and wait for it to be indexed.

import { handleOperationWith } from "@lens-protocol/client/viem";
// …
const result = await createAccountWithUsername(sessionClient, {  username: { localName: "wagmi" },  metadataUri: uri("lens://4f91ca…"),})  .andThen(handleOperationWith(walletClient))  .andThen(sessionClient.waitForTransaction);

See the Transaction Lifecycle guide for more information on how to determine the status of the transaction.

5

Switch to Account Owner

Finally, switch to the Account Owner authentication role to access the newly created Account.

Use the fetchAccount action to retrieve the newly created Account, then call the SessionClient.switchAccount method to switch the authentication role to Account Owner for that Account.

Switch to Account Owner
import { nonNullable } from "@lens-protocol/client";import { fetchAccount } from "@lens-protocol/client/actions";
// …
const result = await createAccountWithUsername(sessionClient, {  username: { localName: "wagmi" },  metadataUri: uri("lens://4f91ca…"),})  .andThen(handleOperationWith(walletClientOrSigner))  .andThen(sessionClient.waitForTransaction)  .andThen((txHash) => fetchAccount(sessionClient, { txHash }).map(nonNullable))  .andThen((account) =>    sessionClient.switchAccount({      account: account.address,    }),  );

That's it—you are now authenticated with the newly created Account.

Account With Restricted Username

If you’re creating an Account with a username in a Namespace that enforces token gating or fees, follow these steps.

1

Log In to Lens

First, authenticate as an Onboarding User.

Use the @lens-protocol/client package to authenticate with the user's wallet.

import { evmAddress } from "@lens-protocol/client";import { signMessageWith } from "@lens-protocol/client/viem";import { client } from "./client";
const authenticated = await client.login({  onboardingUser: {    app: evmAddress("<your-app-address>"),    wallet: signer.address,  },  signMessage: signMessageWith(walletClient),});
if (authenticated.isErr()) {  return console.error(authenticated.error);}
// SessionClient: { ... }const sessionClient = authenticated.value;

The process is explained in detail in the Authentication guide, so we will keep it brief here.

2

Verify Username

Then, verify if the desired username is available and the user's wallet satisfies the Namespace Rules.

Use the canCreateUsername action as follows:

Verify Username Availability
import { evmAddress, RulesSubject } from "@lens-protocol/client";import { canCreateUsername } from "@lens-protocol/client/actions";
import { client } from "./client";
const result = await canCreateUsername(sessionClient, {  localName: "wagmi",  namespace: evmAddress("0x1234…"),  rulesSubject: RulesSubject.Signer,});
if (result.isErr()) {  return console.error(result.error);}
result.value; // CanCreateUsernameResult

Setting rulesSubject to signer means that the Namespace Rules will be checked against the wallet address of the currently logged-in user.

The CanCreateUsernameResult tells you if the logged-in user satisfy the Namespace Rules for creating a username, and if the desired username is available.

Check CanCreateUsernameResult
switch (data.__typename) {  case "NamespaceOperationValidationPassed":    // Creating a username is allowed    break;
  case "NamespaceOperationValidationFailed":    // Creating a username is not allowed    console.log(data.reason);    break;
  case "NamespaceOperationValidationUnknown":    // Validation outcome is unknown    break;
  case "UsernameTaken":    // The desired username is not available    break;}

Where:

  • NamespaceOperationValidationPassed: The logged-in user can create a username under the desired Namespace.

  • NamespaceOperationValidationFailed: Reposting is not allowed. The reason field explains why, and unsatisfiedRules lists the unmet requirements.

  • NamespaceOperationValidationUnknown: The Namespace has one or more unknown rules requiring ad-hoc verification. The extraChecksRequired field provides the addresses and configurations of these rules.

  • UsernameTaken: The desired username is not available.

Treat the NamespaceOperationValidationUnknown as failed unless you intend to support the specific rules. See Namespace Rules for more information.

3

Create Account Metadata

Then, create an Account Metadata object.

The following example demonstrates the most efficient way to create Account Metadata using Grove storage.

Let's assume there is a form where the user can enter their name, bio, and a profile picture. See other Account metadata fields at the bottom of this page.

Example Form
<form onSubmit={onSubmit} id="account-form">  <label htmlFor="picture">Picture:</label>  <input type="file" name="picture" accept="image/png" multiple />
  <label htmlFor="name">Name:</label>  <input type="text" name="name" />
  <label htmlFor="bio">Bio:</label>  <textarea name="bio" rows={3}></textarea>
  <button type="submit">Sign Up</button></form>

Use an instance of the StorageClient to create a Grove folder containing your media files (such as a profile picture), and set the Account Metadata object as the index file.

import type { Resource } from "@lens-chain/storage-client";import { account } from "@lens-protocol/metadata";import { storageClient } from "./storage";
// …
async function onSubmit(event: SubmitEvent) {  event.preventDefault();  const form = event.target as HTMLFormElement;
  const name = form.elements.namedItem("name")?.value || "";  const bio = form.elements.namedItem("bio")?.value || "";  const input = form.elements.namedItem("picture") as HTMLInputElement;
  // …
  const { folder, files } = await storage.uploadFolder(input.files, {    index: (resources: Resource[]) =>      account({        name,        picture: resources[0].uri, // this is a resolved lens://… URI        bio,      }),  });
  // folder.uri will be something like `lens://4f91ca…`}

4

Deploy Account Contract

Then, use the createAccount action to deploy Lens Account smart contract without a username (yet).

import { uri } from "@lens-protocol/client";import { createAccount } from "@lens-protocol/client/actions";
const result = await createAccount(sessionClient, {  metadataUri: uri("<folder.uri>"),});

And, handle the result using the adapter for the library of your choice and wait for it to be indexed.

import { handleOperationWith } from "@lens-protocol/client/viem";
// …
const result = await createAccount(sessionClient, {  metadataUri: uri("lens://4f91ca…"),})  .andThen(handleOperationWith(walletClient))  .andThen(sessionClient.waitForTransaction);

See the Transaction Lifecycle guide for more information on how to determine the status of the transaction.

5

Switch to Account Owner

Then, switch to the Account Owner authentication role to access the newly created Account.

Use the fetchAccount action to retrieve the newly created Account, then call the SessionClient.switchAccount method to switch the authentication role to Account Owner for that Account.

Switch to Account Owner
import { nonNullable } from "@lens-protocol/client";import { fetchAccount } from "@lens-protocol/client/actions";
// …
const result = await createAccount(sessionClient, {  metadataUri: uri("lens://4f91ca…"),})  .andThen(handleOperationWith(walletClientOrSigner))  .andThen(sessionClient.waitForTransaction)  .andThen((txHash) => fetchAccount(sessionClient, { txHash }).map(nonNullable))  .andThen((account) =>    sessionClient.switchAccount({      account: account.address,    }),  );

6

Create Username

Finally, use the createUsername action to assign a username to the newly created Account within the desired restricted Namespace.

Use the createUsername action to create the desired username.

Restricted Namespace
import { evmAddress, RulesSubject } from "@lens-protocol/client";import { createUsername } from "@lens-protocol/client/actions";
const result = await createUsername(sessionClient, {  username: {    localName: "wagmi",    namespace: evmAddress("0x1234…"),  },  rulesSubject: RulesSubject.Signer,});

And, handle the result using the adapter for the library of your choice and wait for it to be indexed.

import { handleOperationWith } from "@lens-protocol/client/viem";
// …
const result = await createUsername(sessionClient, {  username: {    localName: "wagmi",    namespace: evmAddress("0x1234…"),  },  rulesSubject: RulesSubject.Signer,})  .andThen(handleOperationWith(walletClient))  .andThen(sessionClient.waitForTransaction);

See the Transaction Lifecycle guide for more information on how to determine the status of the transaction.

Setting rulesSubject to signer means that the Namespace Rules will be checked against the wallet address of the currently logged-in user.

That's it—you are now authenticated with the newly created Account and have a username in a restricted Namespace.

Advanced Options

Account Metadata Fields

Account Metadata object is a JSON object that contains information about the Account, such as its name, bio, profile picture, and other attributes. It is used to display the Account's information in the Lens ecosystem.

Use the @lens-protocol/metadata package to construct a valid AccountMetadata object:

Example
import { MetadataAttributeType, account } from "@lens-protocol/metadata";
const metadata = account({  name: "Jane Doe",  bio: "I am a photographer based in New York City.",  picture: "lens://4f91cab87ab5e4f5066f878b72…",  coverPicture: "lens://4f91cab87ab5e4f5066f8…",  attributes: [    {      key: "twitter",      type: MetadataAttributeType.STRING,      value: "https://twitter.com/janedoexyz",    },    {      key: "dob",      type: MetadataAttributeType.DATE,      value: "1990-01-01T00:00:00Z",    },    {      key: "enabled",      type: MetadataAttributeType.BOOLEAN,      value: "true",    },    {      key: "height",      type: MetadataAttributeType.NUMBER,      value: "1.65",    },    {      key: "settings",      type: MetadataAttributeType.JSON,      value: '{"theme": "dark"}',    },  ],});

See the Lens Metadata Standards guide for more information on creating and hosting Metadata objects.