Transaction Lifecycle

This guide will help you manage the lifecycle of Lens transactions.


Tiered Transaction Model

The Lens API’s approach to write operations prioritizes user convenience through a tiered transaction model. This model spans from signless transactions to options requiring user signatures and gas fees, offering the best possible experience based on the individual user's circumstances.

There are two classes of operations in this model:

  • Delegable Operations: These operations can be signed by an Account Manager if the user chooses.

  • Restricted Operations: These operations require the user's signature due to their nature.

  • Server Fundable Operations: These operations could be funded by the server if the user is eligible. This is used to facilitate management operations such as creating new Apps, creating Custom Graphs, etc.

The Lens API adapts its operation results based on user eligibility, ensuring users can proceed with the best available options, from the smoothest experience to necessary fallbacks.

Delegable Operations

The tiered transaction model for Delegable Operations is as follows:

1

Signless Sponsored Transaction - Best UX

  • Description: Automatically signed and sent by the Lens API, with gas fees sponsored.

  • Requirements: Available when the user enabled the signless experience, the user is eligible for sponsorship, and the operation is deemed secure for signless execution.

2

Sponsored Transaction Request

  • Description: Requires the user to sign and send a gasless transaction request, powered by ZKsync EIP-712.

  • Requirements: Available if the user is eligible for sponsorship but lacks signless support.

3

Self-Funded Transaction Request - Fallback

  • Description: Requires user signature and gas payment, following a standard EIP-1559 transaction request.

  • Requirements: Used when neither signless nor sponsored transactions are available.

Restricted Operations

The tiered transaction model for Restricted Operations is as follows:

1

Sponsored Transaction Request - Best UX

  • Description: Requires the user to sign and send a gasless transaction request, powered by ZKsync EIP-712.

  • Requirements: Available when the user is eligible for sponsorship.

2

Self-Funded Transaction Request - Fallback

  • Description: Requires user signature and gas payment, following a standard EIP-1559 transaction request.

  • Requirements: Used when the user is not eligible for sponsorship.

Server Fundable Operations

The tiered transaction model for Server Fundable Operations is as follows:

1

Sent Sponsored Transaction - Best UX

  • Description: Automatically signed and sent by the Lens API, with gas fees sponsored.

  • Requirements: Available when the user is eligible for sponsorship and the operation is deemed secure for signless execution.

2

Self-Funded Transaction Request - Fallback

  • Description: Requires user signature and gas payment, following a standard EIP-1559 transaction request.

  • Requirements: Used when the server deems the user as not eligible for sponsorship.

Operation Results

With Operation representing the operation of interest, the <Operation>Result is a union type with the possible outcomes depending on the class of operation:

fragment OperationResult on OperationResult {  ... on OperationResponse {    hash  }
  ... on SponsoredTransactionRequest {    ...SponsoredTransactionRequest  }
  ... on SelfFundedTransactionRequest {    ...SelfFundedTransactionRequest  }
  ... on TransactionWillFail {    reason  }
  # any additional failure scenarions specific to the operation}

OperationResponse being is a placeholder for operation-specific responses like PostResponse, FollowResponse, etc.)

Where:

  • OperationResponse: Indicates that the transaction was successfully sent and returns the transaction hash for further monitoring.

  • SponsoredTransactionRequest: Requests the user to sign and send the transaction, with gas fees covered.

  • SelfFundedTransactionRequest: Requests the user to sign and send the transaction, with the user covering gas fees.

  • TransactionWillFail: This is an omnipresent entry that, if present, indicates that the transaction will fail for a specific reason.

fragment SponsoredTransactionRequest on SponsoredTransactionRequest {  encoded  raw {    ...Eip712TransactionRequest  }}
fragment Eip712TransactionRequest on Eip712TransactionRequest {  type  to  from  nonce  gasLimit  gasPrice  data  value  chainId  customData {    gasPerPubdata    factoryDeps    customSignature    paymasterParams {      paymaster      paymasterInput    }  }}

Transaction Requests

Both SponsoredTransactionRequest and SelfFundedTransactionRequest types include the the typed transaction envelope informations of the transaction request.

For sponsored transactions, this is the typed transaction envelope of a ZKsync EIP-712 transaction request.

Decoding and Sending Transactions

To decode the encoded response, you can use the zksync-ethers package, which allows parsing it into a TransactionLike object for sending via the user's wallet:

zksync-ethers
import { types } from "zksync-ethers";
if (  result.__typename === "SponsoredTransactionRequest" ||  result.__typename === "SelfFundedTransactionRequest") {  const tx: types.TransactionLike = types.Transaction.from(result.encoded);
  await wallet.sendTransaction(tx);}

Using the Raw Values

If you prefer using a different library, you can use the raw values to create the appropriate transaction request. Below is an example using the viem library:

viem
import type { WalletClient } from 'viem';import { sendEip712Transaction, sendTransaction } from 'viem/zksync';
const walletClient: WalletClient = ...;
if (operationResult.__typename === 'SponsoredTransactionRequest') {  await sendEip712Transaction(walletClient, {    account: transaction.raw.from,    chain: transaction.raw.chainId,    data: transaction.raw.data,    gas: BigInt(transaction.raw.gasLimit),    gasPrice: BigInt(transaction.raw.gasPrice),    nonce: transaction.raw.nonce,    paymaster: transaction.raw.customData.paymasterParams?.paymaster,    paymasterInput: transaction.raw.customData.paymasterParams?.paymasterInput,    to: transaction.raw.to,    value: BigInt(transaction.raw.value),  });}
if (operationResult.__typename === 'SelfFundedTransactionRequest') {  await sendTransaction(walletClient, {    account: transaction.raw.from,    chain: transaction.raw.chainId,    data: transaction.raw.data,    gas: BigInt(transaction.raw.gasLimit),    maxFeePerGas: BigInt(transaction.raw.maxFeePerGas),    maxPriorityFeePerGas: BigInt(transaction.raw.maxPriorityFeePerGas),    nonce: transaction.raw.nonce,    to: transaction.raw.to,    type: 'eip1559',    value: BigInt(transaction.raw.value),  });}

As you may have noticed, Viem offers first-class support for ZKsync chains. Your experience may vary depending on the library you choose.

Transaction Monitoring

At this point, whether you received an Operation Response or you sent a Transaction Request via the user's wallet (SponsoredTransactionRequest or SelfFundedTransactionRequest), you should have a transaction hash.

You can poll the transactionStatus query with the transaction hash to monitor the transaction's lifecycle.

It's recommended to poll this query no more frequently than once per second.

query {  transactionStatus(request: { txHash: "0x1234…" }) {    ... on NotIndexedYetStatus {      reason      txHasMined    }
    ... on PendingTransactionStatus {      blockTimestamp    }
    ... on FinishedTransactionStatus {      blockTimestamp    }
    ... on FailedTransactionStatus {      reason      blockTimestamp    }  }}

where:

  • NotIndexedYetStatus: The transaction has not been indexed yet. This could be due to delays in broadcasting to all nodes, not being mined, or events not being picked up by the Lens Indexer.

  • PendingTransactionStatus: The transaction is being processed by the Lens Indexer. This status can take longer for transactions involving metadata URIs, which need to be fetched and parsed.

  • FinishedTransactionStatus: The transaction has been successfully mined and indexed. You can go ahead with the desired user experience.

  • FailedTransactionStatus: The transaction has failed, with the response providing a reason for the failure.