Learn how to provide a gasless social experience for your users.


The Lens API allows you to offer a gasless experience to your users by sponsoring their transaction costs.

Transactions created through a Signless Experience are inherently Sponsored Transactions. This means that the user is not required to sign the transaction, and the Lens API takes care of all associated transaction costs.

To illustrate these concepts, examples from the process of creating a Post will be used. It would be beneficial to have some familiarity with Content Creation.

Broadcast On-Chain

Most operations in Lens involve EVM transactions. These are referred to as on-chain transactions to differentiate them from Momoka transactions.

If you're using the Lens React SDK in your dApp and an operation involves an on-chain transaction, the SDK will automatically use Sponsored Transactions. This feature is enabled by default, requiring no additional effort on your part.

For example, when authoring a Post, the useCreatePost hook does not require any additional configuration.

Example
const { execute, loading, error } = useCreatePost();
// ...
// publish postconst result = await execute({  metadata:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",  reference: {    type: ReferencePolicyType.FOLLOWERS_ONLY  },});
// detect if an early error occurredif (result.isFailure()) {  console.error(result.error.message);  return;}
// the transaction has been broadcasted and is now being monitored by the SDK

In the example above, the Reference policy is used solely to make the hook opt for broadcasting on‑chain, rather than broadcasting on Momoka, as described in the Momoka Publication process. Setting up a Collect Action or any other Open Action would have the same effect.

You also have the option to opt out of the automatic Sponsored Transaction behavior as illustrated in the example below.

Example
// publish postconst result = await execute({  metadata:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",  reference: {    type: ReferencePolicyType.FOLLOWERS_ONLY  },
  // opt out of Sponsored Transactions  sponsored: false,});

See Self-Funded Transactions for a comprehensive explanation of this behavior.

The Lens React SDK also handles transaction monitoring for you. This functionality persists even after a full page refresh or when using the same app across different web browser tabs.

In practical terms, this ensures a seamless and robust user experience. Any operation will be reflected across all tabs or windows of your dApp, even if the user navigates away and returns later.

The same principles applies to React Native integrations via the @lens-protocol/react-native package.

Broadcast On-Momoka

Certain operations in Lens can be performed using the Momoka Data Scaling solution. Momoka transactions are less expensive and offer higher throughput compared to traditional on-chain transactions.

Momoka transactions are inherently Sponsored Transactions. Currently, they are used exclusively for creating Momoka Publications. However, the potential for expanding their use in the future remains open.

LIP-11 explores the possibility of enabling self-funded Momoka transactions. This would permit apps and users to bear their own Momoka transaction costs.

The Lens React SDK determines when it's appropriate to use Momoka for a given publication, in accordance with the Momoka Publication guidelines.

For example, when creating a Post using the useCreatePost hook, if there are no Reference policy restrictions, Collect Action settings, or Open Action module settings, the operation will be broadcasted on Momoka.

Example
const { execute, loading, error } = useCreatePost();
// ...
// publish postconst result = await execute({  metadata:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"});
// detect if an early error occurredif (result.isFailure()) {  window.alert(result.error.message);  return;}
// optional: wait for the transaction to be mined and indexedconst completion = await result.value.waitForCompletion();
// detect if a minining/indexing error occurredif (completion.isFailure()) {  window.alert(completion.error.message);  return;}
// post was createdconst post = completion.value;

If you decide to use a Self-Funded Transaction by setting the sponsored: false flag, the transaction will be broadcasted on-chain.

Example
// publish postconst result = await execute({  metadata:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
  // opt out of Sponsored Transactions  sponsored: false,});

As expected, the user's wallet will cover the gas fees.


Eligibility Criteria

To use Sponsored Transactions through the Lens API, you must meet the following criteria:

  • The Profile executing the operation must be within their rate limits.

  • If an operation involves a custom module, the module must be approved for sponsorship by the Lens Protocol team.

Rate Limits

The Lens Protocol team has set rate limits on the number of sponsored transactions that will be covered for any given user.

Transaction TypeHourly LimitDaily Limit
On-chain3535
Momoka50200

Since Momoka transactions are currently used exclusively for creating Momoka Publications, this effectively sets a limit on the number of Momoka Publications a profile can create within a given timeframe.

You can verify the current rate limits for a user by using either the SDK or the API.

You can utilize the client.wallet.rateLimits method to retrieve the current limits for sponsored transactions associated with the specified address and profile.

Example
import { development, LensClient } from '@lens-protocol/client';
const client = new LensClient({  environment: development,});
const result = await client.wallet.rateLimits({  userAddress: '0xa5653e88D9c352387deDdC79bcf99f0ada62e9c6',});

Low-Signal Profile Limits

The Lens Protocol team has implemented a series of measures to uphold the quality of the Lens ecosystem. These measures are designed to mitigate the impact of spammy profiles on the overall user experience. They include:

  • Machine Learning Algorithms: The Lens backend employs a set of ML algorithms that take into account factors such as follower graphs, content, and other variables to calculate a signal score for each profile.

  • Community Gardeners: This group, comprising Lens community members and Lens Protocol team members, helps identify profiles that negatively impact the overall user experience.

The Lens API imposes further restrictions on the number of sponsored transactions for profiles that fall below a certain signal score threshold, referred to as low-signal profiles. The rate limits for these low-signal profiles are as follows:

Transaction TypeHourly LimitDaily Limit
On-chain00
Momoka1010

Accounts identified as low-signal will need to cover their own gas costs for transactions beyond these limits.

Sponsored Modules

With the introduction of Lens Protocol V2, anyone can now create and use Custom Modules without requiring approval from the Lens Protocol team. This empowers the community to innovate without constraints.

However, Lens API only sponsors transactions involving modules that have undergone a review process. Consequently, operations involving non-reviewed custom modules may not be eligible for sponsored transactions.


Nonce Management

This section is intended for those integrating dApps using the JavaScript SDK or the Lens API directly. If you're using the Lens React SDK, nonce management is automatically taken care of, so you can skip this section.

Overview

In the broadcast on-chain section, we explored the process of creating a sponsored on-chain transaction using EIP-712 Typed Data and signatures. When an operation is broadcast, its signature is directed to a specific Lens Protocol function. Each operation is associated with a unique function that verifies the signature, unpacks the typed data, and carries out the operation on the user's behalf.

To prevent replay attacks on signatures, each operation incorporates a nonce in its EIP-712 Typed Data. This nonce, a sequentially incremented number, tracks the operations performed by a specific signer. It ensures that each signature is unique and can only be processed once. If a nonce is reused or out of sequence, the operation will fail.

The Challenge

When you initiate a sponsored transaction, the Lens API generates the EIP-712 Typed Data based on its current understanding of the blockchain state, which includes the latest known nonce for the signer's address. However, if you initiate another sponsored transaction on the same contract while a previous one is still pending, the nonce in the new transaction's Typed Data will collide with the nonce of the pending transaction. This collision will cause the new transaction to be rejected by the Lens Protocol.

The Solution

If your dApp allows for concurrent operations, you need to manage signature nonces on the consumer side.

You must be authenticated at this point.

The Lens Protocol has three separate contracts that track nonces independently. These contracts are:

  • LensHub: This contract provides all social functionalities, such as posting, commenting, following, and more.

  • TokenHandleRegistry: This contract manages the linking of Lens Handles to Lens Profiles.

  • PublicActProxy: This contract enables the execution of Open Actions, including Collect Actions, directly from a wallet, without the need for a Lens Profile.

For reliable nonce management, it's crucial to stay updated with the latest nonce for the specific Lens contract you're planning to interact with through sponsored transactions.

You can accomplish this with the JavaScript SDK client.wallet.sigNonces method.

const result = await client.wallet.sigNonces();
if (result.isFailure()) {  console.error(result.error);  process.exit(1);}
console.log(result.value);

Alternatively, you can directly use the Lens API userSigNonces query.

query {  userSigNonces {    lensHubOnchainSigNonce    lensPublicActProxyOnchainSigNonce    lensTokenHandleRegistryOnchainSigNonce  }}

In this context:

  • lensHubOnchainSigNonce is the latest nonce for the LensHub contract.

  • lensPublicActProxyOnchainSigNonce is the latest nonce for the PublicActProxy contract.

  • lensTokenHandleRegistryOnchainSigNonce is the latest nonce for the TokenHandleRegistry contract.

The nonce you should consider for an operation depends on the specific operation and whether the user is logged in with a Profile or Wallet-only.

The table below serves as a guide. It illustrates the relationship between the method used to create Typed Data for an operation, the user's authentication status (either Wallet-only or Profile), and the corresponding contract whose nonce should be used as reference for subsequent transactions.

Operation Typed DataWallet-onlyProfile
createLinkHandleToProfileTypedData-TokenHandleRegistry
createUnlinkHandleFromProfileTypedData-TokenHandleRegistry
createFollowTypedData-LensHub
createUnfollowTypedData-LensHub
createSetFollowModuleTypedData-LensHub
createActOnOpenActionTypedDataPublicActProxyLensHub
createLegacyCollectTypedData-LensHub
createOnchainSetProfileMetadataTypedData-LensHub
createChangeProfileManagersTypedData-LensHub
createBlockProfilesTypedData-LensHub
createUnblockProfilesTypedData-LensHub
createOnchainPostTypedData-LensHub
createOnchainCommentTypedData-LensHub
createOnchainQuoteTypedData-LensHub
createOnchainMirrorTypedData-LensHub

To clarify, let's consider a couple of examples:

  • When you're following a Profile (which necessitates Profile authentication), the lensHubOnchainSigNonce should be used as the reference nonce in the Typed Data.

  • When you're collecting a Publication and the user is authenticated with a Wallet only, the lensPublicActProxyOnchainSigNonce should be used as the nonce in the Typed Data.

Next, every time you create EIP-712 Typed Data for a sponsored transaction, you should specify the nonce explicitly by incrementing the latest reference nonce by 1.

const typedDataResult = await client.publication.createOnchainPostTypedData(  {    contentURI:      "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",  },  {    overrideSigNonce: 42 + 1,  });

You should continue this process, incrementing the nonce by 1 for each new sponsored transaction you create.

The sequence diagram below offers a high-level overview of the process. We've intentionally omitted some details to focus on the aspect of nonce management.

That's it—you now have a solid understanding of how to manage nonces for sponsored transactions.