Help & Support

Sponsoring Transactions

Lens allows apps to offer a free user experience to their end-users through Sponsorships.

Lens Sponsorship enables developers to provide a fully configurable, gasless experience for their users. It leverages the ZKsync Paymaster.

After creating and funding a Sponsorship, it can be used to cover gas fees for Lens Protocol transactions, as well as any other transactions on the Lens Chain.

Create Sponsorship

To create a Sponsorship, follow these steps.

You MUST be authenticated as a Builder to create a Sponsorship.

1

Create Metadata

First, create the Sponsorship Metadata object.

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

Example
import { sponsorship } from "@lens-protocol/metadata";
const metadata = sponsorship({  name: "GasPal",});

2

Upload Metadata

Next, upload the Sponsorship Metadata object to a public URI.

import { storageClient } from "./storage-client";
const { uri } = await storageClient.uploadAsJson(metadata);
console.log(uri); // e.g., lens://4f91ca…

This example uses Grove storage to host the Metadata object. See the Lens Metadata Standards guide for more information on hosting Metadata objects.

3

Deploy Contract

Next, deploy the Lens Sponsorship smart contract.

By setting the allowLensAccess flag to true, you are allowing the Lens API to use the Sponsorship to sponsor Lens transactions for users of your Lens App.

Use the createSponsorship action to deploy the Lens Sponsorship smart contract.

deploy-sponsorship.ts
import { uri } from "@lens-protocol/client";import { createSponsorship } from "@lens-protocol/client/actions";
// …
const result = await createSponsorship(sessionClient, {  metadataUri: uri("lens://4f91…"), // the URI from the previous step  allowLensAccess: true,});

4

Handle Result

Then, handle the result using the adapter for the library of your choice:

import { handleOperationWith } from "@lens-protocol/client/viem";
// …
const result = await createSponsorship(sessionClient, {  metadataUri: uri("lens://4f91…"), // the URI from the previous step  allowLensAccess: true,}).andThen(handleOperationWith(walletClient));

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

Fund Sponsorship

To sponsor transactions for your users, you must periodically fund your Lens Sponsorship.

This involves sending native GHO (GRASS on Testnet) to the Lens Sponsorship contract address.

You can accomplish this either manually through a wallet or programmatically using code.

import { ethers } from "ethers";
import { wallet } from "./wallet";
const response = await wallet.sendTransaction({  to: "<SPONSORSHIP_ADDRESS>",  value: ethers.parseEther("100"), // Amount in native tokens});
const receipt = await response.wait();
// funded

Refer to the Lens Chain integration guide for more options on how to integrate with the Lens Chain.

To start using Lens Sponsorship to sponsor Lens transactions for your users, follow these steps.

To simplify the development process on Testnet, if an app Sponsorship contract is not configured, all transactions are sponsored by Lens through a global Sponsorship contract.

1

First, configure your Lens App to use a Sponsorship you previously created.

Use the setAppSponsorship action to set the Sponsorship for your App.

set-app-sponsorship.ts
import { evmAddress } from "@lens-protocol/client";import { setAppSponsorship } from "@lens-protocol/client/actions";
// …
const result = await setAppSponsorship(sessionClient, {  app: evmAddress("0x1234…"),  sponsorship: evmAddress("0x5678…"),});

2

Then, 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 setAppSponsorship(sessionClient, {  app: evmAddress("0x1234…"),  sponsorship: evmAddress("0x5678…"),}).andThen(handleOperationWith(walletClient));

And, ensure the transaction was successful:

Wait for Transaction
const result = await setAppSponsorship(sessionClient, {  app: evmAddress("0x1234…"),  sponsorship: evmAddress("0x5678…"),})  .andThen(handleOperationWith(signer))  .andThen(sessionClient.waitForTransaction);
if (result.isErr()) {  return console.error(result.error);}
// The transaction was successful

3

Finally, implement the Authentication Workflow to be able to authorize end-users for your sponsorship. If this is not implemented, the Lens API will require end-users to cover transaction fees by returning a Self-Funded Transaction Request for any operation involving a transaction.

Since transactions on Testnet fall back to being sponsored by the Lens global Sponsorship if no app Sponsorship is configured, you might not notice any visible difference in the final user experience until deploying to Mainnet, where the full behavior is enforced.

To sponsor any transaction on the Lens Chain using funds from your Sponsorship, follow these steps.

1

First, generate a new private key for the address responsible for approving sponsorship requests (i.e., the signer).

cast wallet new
Successfully created new keypair.Address:     0x8711d4d6B7536D…Private key: 0x72433488d76ffec7a16b…

2

Next, add the signer to your Sponsorship.

You MUST be authenticated as a Builder and be the owner or an admin for the Sponsorship you want to add the signer to.

Use the updateSponsorshipSigners action to add the signer to your Sponsorship.

Add Sponsorship Signer
import { evmAddress } from "@lens-protocol/client";import { updateSponsorshipSigners } from "@lens-protocol/client/actions";
// …
const result = await updateSponsorshipSigners(sessionClient, {  sponsorship: evmAddress("0xe2f2a5C287993345a840db3B0845fbc70f5935a5"),  toAdd: [    {      address: evmAddress("0x8711d4d6B7536D…"),      label: "My Backend System",    },  ],});

3

Then, 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 updateSponsorshipSigners(sessionClient, {  sponsorship: evmAddress("0xe2f2a5C287993345a840db3B0845fbc70f5935a5"),  toAdd: [    {      address: evmAddress("0x8711d4d6B7536D…"),      label: "My Backend System",    },  ],})  .andThen(handleOperationWith(walletClient))  .andThen(sessionClient.waitForTransaction);

4

Finally, implement the logic to sponsor user's transaction that supports your use case.

Here’s an example of a client-side application that sends a request to its backend to generate a sponsored transaction based on specific criteria.

import { parseEip712Transaction, sendEip712Transaction } from "viem/zksync";
import { wallet } from "./wallet";
const request = {  from: wallet.account.address,  to: "0x567890abcdef1234567890abcdef1234567890ab",  value: 100,};
const response = await fetch("http://localhost:3000/sponsor", {  method: "POST",  headers: {    "Content-Type": "application/json",  },  body: JSON.stringify(request),});const { serialized } = await response.json();
// send the transactionconst transaction = parseEip712Transaction(serialized) as any;const hash = await sendEip712Transaction(wallet, transaction);

The backend server listens for incoming requests, utilizes the SponsorshipApprovalSigner to approve the transaction, and sends the approved transaction back to the client.

import express from "express";import type { Address } from "viem";import { serializeTransaction } from "viem/zksync";
import { approver } from "./approver";
const app = express();app.use(express.json());
app.post("/sponsor", async (req, res) => {  try {    const approved = await approver.approveSponsorship({      account: req.body.from as Address,      to: req.body.to as Address,      value: BigInt(req.body.value),    });    res.json({      serialized: serializeTransaction(approved),    });  } catch (err) {    console.error(err);    res.status(500).json({ error: String(err) });  }});
app.listen(3000, () => {  console.log("Server listening on http://localhost:3000");});