Broadcast Onchain Transaction
Broadcasting transactions is free on testnet (Mumbai) but for production (Mainnet) you must be on the whitelist
Broadcasting transactions enables a gasless experience for users when interacting with the Lens Protocol. Unlike using the dispatcher, they must provide a signature through signing a message with their wallet.
Using this approach, you must assemble the typed data for the action the user wants to take using the provides meethods (e.g. createPostTypedData
), the profile wallet must then sign this data and the signature is used to relay the transaction onchain.
Use the transaction broadcasting for post
, comment
, and mirror
related actions.
If the user has the dispatcher enabled, you should use that mechanism over broadcasting.
Broadcast a Transaction
Parameters
- id:
broadcastId
(required- This is the
id
field that is returned from the typed data calls (create*TypedData
).
- This is the
- signature :
Signature
(required)- The signed typed data.
Invocation
const broadcastResult = await lensClient.transaction.broadcastOnchain({
id: YOUR_BROADCAST_ID,
signature: YOUR_SIGNED_TYPE_DATA,
});
mutation BroadcastOnchain($request: BroadcastRequest!) {
broadcastOnchain(request: $request) {
... on RelaySuccess {
txHash
txId
}
... on RelayError {
reason
}
}
}
Response
{
"txHash": "0x000000000",
"txId": "0x01"
}
{
"reason": "REJECTED" // RelayErrorReasons
}
On error, the RelayError
type contains a reason of type RelayErrorReasons
:
export enum RelayErrorReasons {
REJECTED = 'REJECTED',
EXPIRED = 'EXPIRED',
WRONG_WALLET_SIGNED = 'WRONG_WALLET_SIGNED',
NOT_ALLOWED = 'NOT_ALLOWED',
}
Additional Information
Please note you will be given an expiresAt
date if you try to send the broadcast after that has expired it will be rejected. It is worth checking that for edge cases if someone takes a long time accepting the approval modal.
This is the signature without it being split so after your call ethers _signTypedData
as you must do now if using the typed data methods instead of calling ethers utils.splitSignature
you just pass in the full hex string of the signature.
If you see a rejection it is worth allowing them to pay for it themselves so if the error happens use the normal withSig
methods so you can handle gasless and if gasless is ever turned off without your code-breaking. REJECTED
can mean they have used the max allowance in the hour.
Full LensClient Example
import { isRelayerResult } from "@lens-protocol/client";
const typedDataResult = await lensClient.profile.createOnChainSetProfileMetadataTypedData({
metadataURI: YOUR_METADATA_URI,
});
// typedDataResult is a Result object
const data = typedDataResult.unwrap();
// sign with the wallet
const signedTypedData = await wallet._signTypedData(
data.typedData.domain,
data.typedData.types,
data.typedData.value
);
// broadcast
const broadcastResult = await lensClient.transaction.broadcastOnchain({
id: data.id,
signature: signedTypedData,
});
// broadcastResult is a Result object
const broadcastResultValue = broadcastResult.unwrap();
if (!isRelayerResult(broadcastResultValue)) {
console.log(`Something went wrong`, broadcastResultValue);
return;
}
console.log(`Transaction was successfuly broadcasted with txId ${broadcastResultValue.txId}`);
You can also check the status of the transaction using:
// result is a Result object
const result = await lensClient.transaction.status({ txId: broadcastResultValue.txId });
if (!result.isSuccess()) {
console.log(`Something went wrong`, result);
return;
}
const isCompleted = result.value.status === LensTransactionStatusType.Complete;
// or wait till transaction is indexed
await lensClient.transaction.waitUntilComplete({ txId: broadcastResultValue.txId });
Read here about the returned Result type.
Full GraphQL API Example
Querying If The Transaction Has Been Indexed
You need to use https://docs.lens.xyz/docs/has-transaction-been-indexed endpoint to know when it's been indexed. This should be your source of truth and the only thing you call to watch for it to be successful. The main difference between what you should do with this call when using the relay and what you should call when not using the relay is instead of passing in the txHash
into the hasTxHashBeenIndexed
pass in the txId
returned in RelayerResult
this is because our relay will speed up gas on the transactions if the gas prices move or if it's taking too long to be picked up, this, of course, generates a new txHash
and the old one would be dropped. So this is to make sure your client is never stuck in a loop forever. Also because we have to do an extra HTTP call here to find out the status from the transaction id when using txId it will be longer response times than using txHash
so we recommend only calling it once every 1 second.
Updated about 1 month ago