Transaction Monitoring

Learn how to effectively track the status of your Lens transactions.


Numerous Lens operations are carried out as blockchain transactions. Typically, after broadcasting these transactions to the blockchain, you await the transaction receipt to confirm completion. Until this receipt is received, the transaction remains in a pending state. For instance, when using ethers.js, you would typically call the wait method to await the transaction receipt.

Example
const receipt = await tx.wait();

However, when dealing with Lens transactions and retrieving data through the Lens API (either directly or via the SDKs), there are two reasons why simply waiting for the transaction receipt might not be sufficient:

  • The transaction, although mined, might not yet be indexed by the Lens backend.

  • In the case of sponsored transactions, the transaction hash can change. This is because the Lens API, under certain circumstances, can replace the transaction with a new one. For instance, this might be done to speed up the transaction in the event of network congestion.

This page will guide you on how to reliably determine the status of your Lens transactions.

React SDK

This guide uses the useCreatePost hook as an example. However, the same principles apply to the following hooks:

It's assumed that you have some familiarity with these hooks. If not, please refer to the Content Creation documentation for more information.

When you call the execute function with the desired post configuration, the hook returns a Result object.

Example
const { execute, loading, error } = useCreatePost();
// ...
// publish postconst result = await execute({  metadata:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",});

This object allows to discriminate between:

  • success: the transaction was broadcasted, though not necessarily mined or indexed yet

  • failure: a failure occurred before or while broadcasting the transaction

Refer to the Error Handling section for a detailed guide on using the Result<T, E> for effective error management.

For the purpose of this guide, we will focus on identifying if an error occurred and logging any errors to the console.

Example
// detect if an early error occurredif (result.isFailure()) {  console.error(result.error.message);  return;}
// thanks to the return statement above we can safely assume// that the result is a success and use its valueconst value = result.value;

In the specific case of useCreatePost the success value of the execute result is a:

types.ts
/** * An object representing the result of a post creation. */type PostAsyncResult = AsyncTransactionResult<Post>;

This is a specialized version of the AsyncTransactionResult generic type:

types.ts
/** * An object that represents the result of an async transaction. */type AsyncTransactionResult<TValue> = {  /**   * Allows to wait for the transaction until its full completion.   */  waitForCompletion(): PromiseResult<TValue, TransactionError>;};

The waitForCompletion method simplifies the process of waiting for a transaction to fully complete, regardless of the specific method used to create the transaction (signless, sponsored, self-funded, Momoka, etc).

// wait for the transaction to be mined and indexedconst completion = await value.waitForCompletion();

The completion object is another Result<T, E> object that allows you to distinguish between:

  • success: the transaction was successfully mined and indexed

  • failure: a failure occurred during the mining or indexing of the transaction

Example
// detect if a minining/indexing error occurredif (completion.isFailure()) {  switch (completion.error.reason) {    case TransactionErrorReason.INDEXING_TIMEOUT:      console.error(        "The tx was broadcasted but it was not indexed within the expected timeout"      );      break;
    case TransactionErrorReason.MINING_TIMEOUT:      console.error(        "The tx was broadcasted but it was not mined within the expected timeout"      );      break;
    case TransactionErrorReason.REVERTED:      console.error("The tx was reverted");      break;
    case TransactionErrorReason.UNKNOWN:      console.error("A not recognized failure");      break;  }  return;}
// Due to the return statement above, we can safely assume// that the completion result is a success and utilize its valueconst post = completion.value;console.log(`Post ID: ${post.id}`);

JavaScript SDK

Numerous methods in LensClient return an object of type RelaySuccessFragment which contains a unique txId that can be used to monitor the status of a sponsored Lens transaction.

types.ts
type RelaySuccessFragment = {  txHash: string | null;  txId: string;};

This guide uses the client.publication.postOnchain method. However, the principles discussed apply to all methods that return a RelaySuccessFragment object.

It's assumed that you have some familiarity with this method. If not, please refer to the Content Creation documentation for more information.

Example
const result = await client.publication.postOnchain({  contentURI:    "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",});

The result object is a Result<T, E> type that allows you to distinguish between:

  • success: the operation was accepted by the Lens backend

  • failure: an authentication error occurred

Refer to the Error Handling section for a detailed guide on using the Result<T, E> for effective error management.

For the purpose of this guide, we will focus on identifying if an error occurred and logging any errors to the console.

Example
if (result.isFailure()) {  console.error(result.error.message);  process.exit(1);}
// Due to the exit call above, we can safely assume// that the result is a success and utilize its valueconst value = result.value;

The value can either be a RelaySuccessFragment or a LensProfileManagerRelayErrorFragment object. You can use the isRelaySuccess type guard to differentiate between the two.

Example
if (!isRelaySuccess(value)) {  console.error("Something went wrong", value.reason);  process.exit(1);}
// Due to the exit call above, we can safely assume// that the value is a RelaySuccessFragment beyond this point

You can use the client.transaction.status method to check the status of the transaction.

Example
const status = await client.transaction.status({  forTxId: value.txId,});
if (status === null) {  console.error("The transaction was not found");  process.exit(1);}

If all you have is a transaction hash, you can invoke client.transaction.status({ forTxHash: '0x...' }) with it.

The status object, if not null, is a LensTransactionResultFragment:

types.ts
type LensTransactionResultFragment = {  status: LensTransactionStatusType;  txHash: string;  reason: LensTransactionFailureType | null;  extraInfo: string | null;};

Here's what each field represents:

  • status: This is the status of the transaction. It can be:
    • COMPLETE: The transaction was mined and indexed, including metadata in case of operations involving metadata URI(s).

    • FAILED: The transaction failed to mine or to be indexed. See reason and extraInfo for more details.

    • OPTIMISTICALLY_UPDATED: The transaction might not have been mined yet, but the Lens backend has optimistically updated its state. Therefore, it can be safe to fetch relevant data.

    • PROCESSING: The transaction is still being processed by the Lens backend.

  • txHash: This is the transaction hash. It gets updated if the transaction was replaced by the Lens API.

  • reason: This is the reason for the failure, if any.

  • extraInfo: This is additional information about the failure, if any.

You can also use the client.transaction.waitUntilComplete method to actively wait for the transaction to be mined and indexed.

Example
const outcome = await client.transaction.waitUntilComplete({  forTxId: value.txId,});
if (outcome === null) {  console.error("The transaction was not found");  process.exit(1);}

The method has a maximum timeout of 60 seconds, after which it throws a TransactionPollingError.

In this scenario, if outcome is not null, the outcome.status can only be COMPLETE, FAILED, or OPTIMISTICALLY_UPDATED.

Similar to client.transaction.status, you can invoke client.transaction.waitUntilComplete({ forTxHash: '0x...' }) with a transaction hash if your Lens transaction was not sponsored by the Lens backend.

API

Numerous API mutations return a RelaySuccess object. This object contains a unique txId scalar, which can be used to track the status of a sponsored Lens transaction.

RelaySuccess.graphql
type RelaySuccess {  txHash: TxHash  txId: TxId!}

This guide uses the postOnchain mutation as an example. However, the same principles apply to all mutations that return a RelaySuccess type.

It's assumed that you have some familiarity with this mutation. If not, please refer to the Content Creation documentation for more information.

mutation {  postOnchain(request: { contentURI: "<metadata-uri>" }) {    ... on RelaySuccess {      txId      txHash    }    ... on LensProfileManagerRelayError {      reason    }  }}

You can subsequently use the lensTransactionStatus query to verify the status of the transaction.

query {  lensTransactionStatus(request: { forTxId: "c30869d4-9a3a-4373-b5dc-811ea393bdf5"}) {    status    txHash    reason    extraInfo  }}

where:

  • status: This is the status of the transaction. It can be:
    • COMPLETE: The transaction was mined and indexed, including metadata in case of operations involving metadata URI(s).

    • FAILED: The transaction failed to mine or to be indexed. See reason and extraInfo for more details.

    • OPTIMISTICALLY_UPDATED: The transaction might not have been mined yet, but the Lens backend has optimistically updated its state. Therefore, it can be safe to fetch relevant data.

    • PROCESSING: The transaction is still being processed by the Lens backend.

  • txHash: This is the transaction hash. It gets updated if the transaction was replaced by the Lens API.

  • reason: This is the reason for the failure, if any.

  • extraInfo: This is additional information about the failure, if any.

If your Lens transaction was not sponsored by the Lens backend, you can invoke lensTransactionStatus(request: { forTxHash: '0x...' }) with a transaction hash.


Troubleshooting

The following tools can assist you in troubleshooting your Lens transactions.

Resolving Tx Hash

If you have a transaction ID and need the transaction hash for debugging purposes, for example on Polygonscan, you can use the following method.

You can use the client.transaction.txIdToTxHash method to retrieve the transaction hash associated with a specific relay transaction ID.

Example
const txHash = await client.transaction.txIdToTxHash('c30869d4-9a3a-4373-b5dc-811ea393bdf5');

Publication Metadata

One common reason for publication failure is invalid metadata. In the following section, we'll demonstrate how to verify the validity of a metadata object or URI.

If you're using the @lens-protocol/metadata package to create your metadata object, there's no need to use this functionality from your app, as the package will validate the metadata for you.

The client.publication.validateMetadata method can be used to validate a metadata object or URI.

const result = await client.publication.validateMetadata({  json: JSON.stringify(metadata),});

which returns a PublicationValidateMetadataResultFragment object:

type PublicationValidateMetadataResultFragment = {  valid: boolean;  reason: string | null;};

where:

  • valid: This is a boolean value. If the metadata is valid, it returns true; otherwise, it returns false.

  • reason: This is a developer-friendly string that describes the reason for the failure, if any.