Subscribing to SNS notifications
Lens pushes key notification events to sns
notification service so third party providers can hook onto them really easily.
Please note this notification service is to purely push data to your server, it does not check for nasty data or bad content. The notification service just broadcasts what the chain says. It is down to your company to put whatever extra content checks you need to put in place to conform to your standards.
How to authenticate with the sns
notification service
sns
notification serviceYou need to supply us a webhook URL to sns
(one for mainnet and one for testnet) to authenticate with and do a handshake request. This code also listens to the incoming notifications. Example is shown below.
How to listen to the events from sns
sns
This example is given in node and express you can of course use different tech to do this.
import bodyParser from 'body-parser';
import express from 'express';
import fetch from 'node-fetch';
const app = express();
const port = 8080;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/lens/notifications', async (req, res) => {
const buffers = [];
for await (const chunk of req) {
buffers.push(chunk);
}
const data = Buffer.concat(buffers).toString();
// example https://docs.aws.amazon.com/connect/latest/adminguide/sns-payload.html
const payload = JSON.parse(data);
// if you already done the handshake you will get a Notification type
// example below: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
// {
// "Type" : "Notification",
// "MessageId" : "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324",
// "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
// "Subject" : "My First Message",
// "Message" : "Hello world!",
// "Timestamp" : "2012-05-02T00:54:06.655Z",
// "SignatureVersion" : "1",
// "Signature" : "EXAMPLEw6JRN...",
// "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
// "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96"
// }
if (payload.Type === 'Notification') {
console.log('sns message is a notification ', payload);
console.log('------------------------------------------------------');
console.log('------------------------------------------------------');
console.log('------------------------------------------------------');
res.sendStatus(200);
return;
}
// only need to do this the first time this is doing an handshake with the sns client
// example below: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
// {
// "Type" : "SubscriptionConfirmation",
// "MessageId" : "165545c9-2a5c-472c-8df2-7ff2be2b3b1b",
// "Token" : "2336412f37...",
// "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
// "Message" : "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
// "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37...",
// "Timestamp" : "2012-04-26T20:45:04.751Z",
// "SignatureVersion" : "1",
// "Signature" : "EXAMPLEpH+DcEwjAPg8O9mY8dReBSwksfg2S7WKQcikcNKWLQjwu6A4VbeS0QHVCkhRS7fUQvi2egU3N858fiTDN6bkkOxYDVrY0Ad8L10Hs3zH81mtnPk5uvvolIC1CXGu43obcgFxeL3khZl8IKvO61GWB6jI9b5+gLPoBc1Q=",
// "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"
// }
if (payload.Type === 'SubscriptionConfirmation') {
const url = payload.SubscribeURL;
const response = await fetch(url);
if (response.status === 200) {
console.log('Subscription confirmed');
console.log('------------------------------------------------------');
console.log('------------------------------------------------------');
console.log('------------------------------------------------------');
res.sendStatus(200);
return;
} else {
console.error('Subscription failed');
res.sendStatus(500);
return;
}
}
console.log('Received message from sns', payload);
// if it gets this far it is a unsubscribe request
// {
// "Type" : "UnsubscribeConfirmation",
// "MessageId" : "47138184-6831-46b8-8f7c-afc488602d7d",
// "Token" : "2336412f37...",
// "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
// "Message" : "You have chosen to deactivate subscription arn:aws:sns:us-west-2:123456789012:MyTopic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.",
// "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37fb6...",
// "Timestamp" : "2012-04-26T20:06:41.581Z",
// "SignatureVersion" : "1",
// "Signature" : "EXAMPLEHXgJm...",
// "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"
// }
});
app.listen(port, () => console.log('Sns notification listening on port ' + port + '!'));
Signatures
You should verify the authenticity of a notification, subscription confirmation, or unsubscribe confirmation message sent by Amazon SNS. Using information contained in the Amazon SNS message, your endpoint can recreate the string to sign and the signature so that you can verify the contents of the message by matching the signature you recreated from the message contents with the signature that Amazon SNS sent with the message.
To find out more info go https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html
const bent = require('bent');
const getBuffer = bent('buffer');
const crypto = require('crypto');
const debug = require('debug')('verify-aws-sns-signature');
const parseUrl = require('parse-url');
const assert = require('assert');
async function validatePayload(payload) {
const {
SigningCertURL,
Signature,
Message,
MessageId,
SubscribeURL,
Subject,
Timestamp,
Token,
TopicArn,
Type,
} = payload;
// validate SubscribeURL
const url = parseUrl(SigningCertURL);
assert.ok(
/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/.test(url.resource),
`SigningCertURL host is not a valid AWS SNS host: ${SigningCertURL}`
);
try {
debug(`retrieving AWS certificate from ${SigningCertURL}`);
const x509 = await getBuffer(SigningCertURL);
const publicKey = crypto.createPublicKey(x509);
const signature = Buffer.from(Signature, 'base64');
const stringToSign = (
'Notification' === Type
? [{ Message }, { MessageId }, { Subject }, { Timestamp }, { TopicArn }, { Type }]
: [
{ Message },
{ MessageId },
{ SubscribeURL },
{ Timestamp },
{ Token },
{ TopicArn },
{ Type },
]
).reduce((acc, el) => {
const key = el.keys()[0];
acc += key + '\n' + el[key] + '\n';
}, '');
debug(`string to sign: ${stringToSign}`);
const verified = crypto.verify(
'sha1WithRSAEncryption',
Buffer.from(stringToSign, 'utf8'),
publicKey,
signature
);
debug(`signature ${verified ? 'has been verified' : 'failed verification'}`);
return verified;
} catch (err) {
return false;
}
}
module.exports = { validatePayload };
Messages
This main thing you will care about is the Message
field. This will contain the information we will be pushing to sns
. This is a JSON.stringify
object and will always be an object.
This section will purely talk about the Message
field and all examples will show the response type for that and not the entire sns
object above. Also, the guide will of JSON.parse
the string already to make it really easy to understand. The data in the properties is just mock data to show the examples.
This guide will assume you already have a understanding for what the events
emit on the protocol and what each data property is.
All the messages will be shaped as:
export interface PublishMessage {
type: SnsMessageTypes;
data: any;
}
The type
maps to the response type you get back which will defined in this guide.
COLLECT_MODULE_allowlisted
This emits when a collect module is allowlisted or unallowlisted.
interface Response {
collectModule: string;
collectModuleName: string;
allowlisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"collectModule": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"collectModuleName": "LimitedFeeCollectModule",
"allowlisted": true,
"timestamp": 1656073676000,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
INDEXER_MODULE_GLOBALS_TREASURY_FEE_SET
This emits when the module global treasury fee is set
interface Response {
prevTreasuryFee: string;
newTreasuryFee: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"prevTreasuryFee": "0x0000000000000000001",
"newTreasuryFee": "0x0000000000000000001",
"timestamp": 1656073676000,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
Profile related
FOLLOW_NFT_DEPLOYED
This emits when the follow nft is deployed (remember it is lazy loaded meaning only on the first follow is it deployed).
interface Response {
profileId: string;
followNFT: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileId": "0x01",
"followNFT": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"timestamp": 1656073676000,
"logIndex": 1,
"transactionHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"transactionIndex": 8
}
METADATA_PROFILE_COMPLETE
This emits a notification when the profile metadata has been snapshotted and indexed into the API.
Refer to the @lens-protocol/metadata
package for the exact metadata type definition.
import { ProfileMetadata } from '@lens-protocol/metadata';
import { ProfileMetadata as LegacyProfileMetadata } from '@lens-protocol/metadata/legacy';
interface Response {
profileId: string;
// the metadata extracted for you
metadata: ProfileMetadata | LegacyProfileMetadata;
// the snapshot s3 url for fast fetching
snapshotLocation: string;
}
Response example
{
"profileId": "0x06",
"metadata": {
"version": "1.0.0",
"metadata_id": "cf40ca2b-945d-4fd6-9a5c-dfdfe07a7008",
"name": "josh wagmi",
"attributes": []
},
"snapshotLocation": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/profile_metadata/0x06.json"
}
METADATA_PROFILE_FAILED
This emits a notification when the profile metadata has failed to be snapshotted. This could be because the metadata is invalid or unreachable etc. The snapshot will retry for 20 minutes if it is unreachable before it marks it as failed. If the metadata is invalid it won't retry and mark as failed with an error reason.
interface Response {
profileId: string;
// the reason why it failed
errorReason: string;
// if it was unreachable for 20 minutes
timeoutRequest: boolean;
}
Response example
{
"profileId": "0x06",
"errorReason": "metadata is missing the metadata_id",
"timeoutRequest": false
}
METADATA_PROFILE_IMAGE_NFT_REVOKED
This emits a notification when the NFT image a profile has added as a verified NFT profile picture is not owned by the profile owner anymore.
interface Response {
// all profiles which now have been revoked the NFT on chain image. The image still lives in our snapshot
// but when the API brings it back now its not classed as an NFT image anymore.
profileIds: string[];
}
Response example
{
"profileIds": ["0x06", "0x02"]
}
PROFILE_BLOCKED
This emits when a profile was blocked by another profile.
interface Response {
byProfileId: string;
idOfProfileBlocked: string;
transactionExecutor: string;
timestamp: string;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"byProfileId": "0x01",
"idOfProfileBlocked": "0x02",
"transactionExecutor": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
PROFILE_UNBLOCKED
This emits when a profile was unblocked by another profile.
interface Response {
byProfileId: string;
idOfProfileUnblocked: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"byProfileId": "0x01",
"idOfProfileUnblocked": "0x02",
"transactionExecutor": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
PROFILE_CREATED
This emits when someone has created a new profile.
interface Response {
profileId: string;
creator: string;
to: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileId": "0x0f",
"creator": "0x1e9B092A67c2513DF0217A3dd5B9f13286E3D10D",
"to": "0x1e9B092A67c2513DF0217A3dd5B9f13286E3D10D",
"txHash": "0xee3e49b8fb85979b8885d76b9d7aa79f8e4cdded303dd3c40d993761b94957d9",
"txIndex": 0,
"logIndex": 1
}
PROFILE_CREATOR_ALLOWLISTED
This emits when someone has allowlisted a profile creator.
interface Response {
profileCreator: string;
allowlisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileCreator": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
"allowlisted": true,
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
PROFILE_FOLLOWED
This emits when someone has follow a profile.
interface Response {
followerProfileId: string;
idOfProfileFollowed: string;
followTokenIdAssigned: string;
followModuleData: string;
processFollowModuleReturnData: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"followerProfileId": "0x0f",
"idOfProfileFollowed": "0x02",
"followTokenIdAssigned": "0x08",
"followModuleData": "0x",
"processFollowModuleReturnData": "0x",
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1694520655000,
"txHash": "0xb8c426ac985e329869b23210ff3ac27e34fc7eb48eee23a998d2ae6708e069c3",
"txIndex": 71,
"logIndex": 400
}
PROFILE_UNFOLLOWED
This emits when someone has unfollowed a profile.
interface Response {
unfollowerProfileId: string;
idOfProfileUnfollowed: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"unfollowerProfileId": "0x0f",
"idOfProfileUnfollowed": "0x02",
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1694520655000,
"txHash": "0xb8c426ac985e329869b23210ff3ac27e34fc7eb48eee23a998d2ae6708e069c3",
"txIndex": 71,
"logIndex": 400
}
PROFILE_GUARDIAN_STATE_CHANGED
This emits when the profile guardian state has changed.
interface Response {
owner: string;
enabled: boolean;
tokenGuardianDisablingTimestamp: number;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"owner": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"enabled": true,
"tokenGuardianDisablingTimestamp": 1656073958000,
"timestamp": 1656073676000,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
PROFILE_HANDLE_LINKED
This emits when the handle was linked to the profile.
interface TokenInfo {
id: string,
collection: string,
}
interface Response {
handle: TokenInfo;
token: TokenInfo;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROFILE_HANDLE_MINTED
This emits when the new handle was minted.
interface Response {
handle: string;
namespace: string;
handleId: string;
to: number;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROFILE_HANDLE_UNLINKED
This emits when the handle was unlinked from the profile.
interface TokenInfo {
id: string,
collection: string,
}
interface Response {
handle: TokenInfo;
token: TokenInfo;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROFILE_MANAGER_CHANGED
This emits when the profile manager has changed.
interface Response {
delegatorProfileId: string;
configNumber: string;
delegatedExecutors: string[];
approvals: boolean[];
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROFILE_MANAGER_CONFIG_APPLIED
This emits when the profile manager config has been applied.
interface Response {
delegatorProfileId: string;
configNumber: string[];
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROFILE_METADATA_SET
This emits when someone has updated metadata for the profile. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be updated on our API. We will also broadcast this event to you so you can see what is happening in real time.
Look at METADATA_PROFILE_COMPLETE
and METADATA_PROFILE_FAILED
notifications to hook onto these states.
interface Response {
profileId: string;
// the metadata contains normally an IPFS or weblink which resolves the metadata
metadata: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileId": "0x01",
"metadata": "ipfs://Qmeu6u6Ta5qeCf6mw3zVoe9pMus96cX6eZT6dnRQKDStBT",
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
PROFILE_TRANSFERRED
This emits when profile ownership was transferred to a different wallet.
interface Response {
from: string;
to: string;
tokenId: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Publication related
POST_CREATED
This emits when someone has posted a new publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.
Look at METADATA_PUBLICATION_COMPLETE
and METADATA_PUBLICATION_FAILED
notifications to hook onto these states.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
isMomoka: boolean;
// will be defined if its a momoka post
momokaProof?: string
postParams: {
profileId: string;
contentURI: string;
// will be empty array if not action modules set
actionModules: string[];
// will be empty array if not action modules set
actionModulesInitDatas: string[];
// will null if not set
referenceModule: string | null;
// will be null if not set
referenceModuleInitData: string | null;
}
pubId: string;
// will be empty array if not action modules set
actionModulesInitReturnDatas: string[];
// will be empty array if not action modules set
referenceModuleInitReturnData: string | null;
transactionExecutor: string;
timestamp: number;
// will not be defined if its a momoka post
logIndex?: number
txHash?: string;
txIndex?: number;
}
Response example
{
"serverPubId":"0x15-0x01",
"isMomoka":false,
"pubId":"0x01",
"postParams":{
"profileId": "0x12",
"contentURI":"ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
"actionModules":[],
"actionModulesInitDatas":[],
"referenceModule":null,
"referenceModuleInitData":null
},
"actionModulesInitReturnDatas":[],
"referenceModuleInitReturnData":null,
"transactionExecutor":"0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp":1694545573000,
"txHash":"0x359826e89fb80b6a7a6a586e9e0c8b3a80c5c797e2ec9bc96e7c7ed31897cabe",
"txIndex":18,
"logIndex":117
}
COMMENT_CREATED
This emits when someone has commented on a publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.
Look at METADATA_PUBLICATION_COMPLETE
and METADATA_PUBLICATION_FAILED
notifications to hook onto these states.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
serverPubIdPointer: string;
isMomoka: boolean;
// will be defined if its a momoka comment
momokaProof?: string
commentParams: {
profileId: string;
contentURI: string;
pointedProfileId: string;
pointedPubId: string;
referrerProfileIds: string[];
referrerPubIds: string[];
// will be empty array if not action modules set
actionModules: string[];
// will be empty array if not action modules set
actionModulesInitDatas: string[];
// will null if not set
referenceModule: string | null;
// will be null if not set
referenceModuleData: string | null;
// will be null if not set
referenceModuleInitData: string | null;
}
pubId: string;
// will be empty array if not action modules set
actionModulesInitReturnDatas: string[];
// will null if not set
referenceModuleInitReturnData: string | null;
// will null if not set
referenceModuleReturnData: string | null;
transactionExecutor: string;
timestamp: number;
// will not be defined if its a momoka comment
logIndex?: number
txHash?: string;
txIndex?: number;
}
Response example
{
"serverPubId": "0x15-0x02",
"serverPubIdPointer": "0x03-0x36",
"isMomoka": false,
"pubId": "0x02",
"commentParams": {
"profileId": "0x12",
"contentURI": "ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
"pointedProfileId": "0x03",
"pointedPubId": "0x36",
"referrerProfileIds": [],
"referrerPubIds": [],
"referenceModuleData": null,
"actionModules": [],
"actionModulesInitDatas": [],
"referenceModule": null,
"referenceModuleInitData": null
},
"referenceModuleReturnData": null,
"actionModulesInitReturnDatas": [],
"referenceModuleInitReturnData": null,
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1694545895000,
"txHash": "0xde1499c7c551f7602a3a632b622e1cf7e94a71364e8f80c9173f839a32bf187b",
"txIndex": 16,
"logIndex": 127
}
MIRROR_CREATED
This emits when a profile has mirrored a publication. This inherits the mirrored publication metadata. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.
Look at METADATA_PUBLICATION_COMPLETE
and METADATA_PUBLICATION_FAILED
notifications to hook onto these states.
// TODO: Update this once mirror has own metadata
Please note mirror
is different to post
and comment
in terms of it doesn't have any metadata linked to it. You must on your end link the publication it has mirrored to the mirror itself to know when its snapshotted and viewable.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
serverPubIdPointer: string;
isMomoka: boolean;
// will be defined if its a momoka mirror
momokaProof?: string
mirrorParams: {
profileId: string;
pointedProfileId: string;
pointedPubId: string;
referrerProfileIds: string[];
referrerPubIds: string[];
// will be null if not set
referenceModuleData: string | null;
}
pubId: string;
// will null if not set
referenceModuleReturnData: string | null;
transactionExecutor: string;
timestamp: number;
// will not be defined if its a momoka mirror
logIndex?: number
txHash?: string;
txIndex?: number;
}
Response example
{
"serverPubId": "0x15-0x03",
"serverPubIdPointer": "0x03-0x36",
"isMomoka": false,
"pubId": "0x03",
"mirrorParams": {
"profileId": "0x12",
"pointedProfileId": "0x36",
"pointedPubId": "0x24",
"referrerProfileIds": [],
"referrerPubIds": [],
"referenceModuleData": null
},
"referenceModuleReturnData": null,
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1694546347000,
"txHash": "0x3d4253698cc528b7178ebdc57619e9f3e2867c0515e2ebce06851f8aa339a776",
"txIndex": 9,
"logIndex": 79
}
QUOTE_CREATED
This emits when someone has quoted a publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.
Look at METADATA_PUBLICATION_COMPLETE
and METADATA_PUBLICATION_FAILED
notifications to hook onto these states.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
serverPubIdPointer: string;
isMomoka: boolean;
// will be defined if its a momoka quote
momokaProof?: string
quoteParams: {
profileId: string;
contentURI: string;
pointedProfileId: string;
pointedPubId: string;
referrerProfileIds: string[];
referrerPubIds: string[];
// will be empty array if not action modules set
actionModules: string[];
// will be empty array if not action modules set
actionModulesInitDatas: string[];
// will null if not set
referenceModule: string | null;
// will be null if not set
referenceModuleData: string | null;
// will be null if not set
referenceModuleInitData: string | null;
}
pubId: string;
// will be empty array if not action modules set
actionModulesInitReturnDatas: string[];
// will null if not set
referenceModuleInitReturnData: string | null;
// will null if not set
referenceModuleReturnData: string | null;
transactionExecutor: string;
timestamp: number;
// will not be defined if its a momoka quote
logIndex?: number
txHash?: string;
txIndex?: number;
}
Response example
{
"serverPubId": "0x15-0x02",
"serverPubIdPointer": "0x03-0x36",
"isMomoka": false,
"pubId": "0x02",
"quoteParams": {
"profileId": "0x12",
"contentURI": "ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
"pointedProfileId": "0x03",
"pointedPubId": "0x36",
"referrerProfileIds": [],
"referrerPubIds": [],
"referenceModuleData": null,
"actionModules": [],
"actionModulesInitDatas": [],
"referenceModule": null,
"referenceModuleInitData": null
},
"referenceModuleReturnData": null,
"actionModulesInitReturnDatas": [],
"referenceModuleInitReturnData": null,
"transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
"timestamp": 1694545895000,
"txHash": "0xde1499c7c551f7602a3a632b622e1cf7e94a71364e8f80c9173f839a32bf187b",
"txIndex": 16,
"logIndex": 127
}
METADATA_PUBLICATION_COMPLETE
This emits a notification when the publication metadata has been snapshotted and indexed into the API.
Refer to the @lens-protocol/metadata
package for the exact metadata type definition.
import { PublicationMetadata } from '@lens-protocol/metadata';
import { PublicationMetadata as LegacyPublicationMetadata } from '@lens-protocol/metadata/legacy';
interface Response {
profileId: string;
pubId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
// this is the mirror publication id
serverPubId: string;
// the metadata extracted for you
metadata: PublicationMetadata | LegacyPublicationMetadata;
// the snapshot url for fast fetching
snapshotLocation: string;
}
Response example
{
"profileId": "0x06",
"pubId": "0x02",
"serverPubId": "0x06-0x02",
"metadata": {
"description":"My text Description",
"external_url":"https://mytext.com",
"name":"My text3",
"image":"https://text.com/image.png",
"$schema":"https://json-schemas.lens.dev/publications/text-only/3.0.0.json",
"lens":{
"id":"1030ee6e-51cb-4a09-a74a-abdccc6ef890"
},
"appId":"my-app-id",
"content":"My text Content",
"locale":"en-US",
"tags":["text"],
"mainContentFocus":"TEXT_ONLY"
},
"snapshotLocation": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/public/0x06-0x02.json"
}
METADATA_PUBLICATION_FAILED
This emits a notification when the publication metadata has failed to be snapshotted. This could be because the metadata is invalid or unreachable etc. The snapshot will retry for 20 minutes if it is unreachable before it marks it as failed. If the metadata is invalid it won't retry and mark as failed with a valid error reason.
interface Response {
profileId: string;
pubId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
// this is the mirror publication id
serverPubId: string;
// the reason why it failed
errorReason: string;
// if it was unreachable for 20 minutes
timeoutRequest: boolean;
}
Response example
{
"profileId": "0x06",
"pubId": "0x02",
"serverPubId": "0x06-0x02",
"errorReason": "metadata is missing the metadata_id",
"timeoutRequest": false
}
PROFILE_MENTIONED
This emits a notification when a profile has been mentioned in a publication. This is used to notify the profile that they have been mentioned in a publication.
interface Response {
profileIdPointed: string;
pubIdPointed: string;
// pub id are counters of the publication, so they clash with profiles
// on the server, we build up our publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
// this will be the publication the mention was created on
serverPubIdPointer: string
handle: string;
profileId: string;
content: string;
contentURI: string;
snapshotLocation: string;
timestamp: Date;
}
PUBLICATION_ACTED
This emits when a profile acted on a publication.
interface Response {
publicationActionParams: {
publicationActedProfileId: string;
publicationActedId: string;
actorProfileId: string;
referrerProfileIds: string[];
referrerPubIds: string[];
actionModuleAddress: string;
actionModuleData: string;
};
actionModuleReturnData: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PUBLICATION_COLLECTED
This emits when a publication is collected.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
collectedProfileId: string;
collectedPubId: string;
collectorProfileId: string;
nftRecipient: string;
collectActionData: string;
collectActionResult: string;
collectNFT: string;
tokenId: string;
collectNFT: string;
collectNFT: string;
transactionExecutor: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PUBLICATION_REACTION_ADDED
This emits a notification when a profile has added a reaction to a publication. This will also add as a toggle so what we mean by that is if someone called UPVOTE
then pressed DOWNVOTE
the last UPVOTE
is now not valid. A reaction a profile does on a publication is only allowed to be 1.
interface Response {
profileId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
type: string;
// unix timestamp
reactedAt: number;
}
PUBLICATION_REACTION_REMOVED
This emits a notification when a profile removes a reaction on a publication.
interface Response {
profileId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
type: string;
}
PUBLICATION_HIDDEN
This emits a notification when a profile has hidden a publication. Please note the API still returns these publication to not break threads but hides the content
and media
of it. These do not get returned in the timelines, search, profiles and explore queries though.
interface Response {
profileId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
}
PUBLICATION_REPORTED
This emits a notification when a publication is reported.
interface Response {
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
profileId: string;
reason: string;
subreason: string;
additionalComments: string | null;
}
COLLECT_NFT_TRANSFERRED
This emits when the collect nft is transferred to a new owner, remember this is emitted when minted as well.
interface Response {
profileId: string;
pubId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
// each collect NFT minted has a token id the first one is 1 then 2 just like a normal NFT project
collectNFTId: string;
from: string;
to: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Protocol related
COLLECT_NFT_DEPLOYED
This emits when the collect nft is deployed (remember it is lazy loaded meaning only on the first collect is it deployed).
interface Response {
profileId: string;
pubId: string;
// pub id are counters of the publication so they clash with profiles
// on the server we build up our own publication id to allow it to be searchable
// this is {profileId}-{pubId} and is used in all our API calls and responses
serverPubId: string;
collectNFT: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileId": "0x01",
"pubId": "0x02",
"serverPubId": "0x01-0x02",
"collectNFT": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
COLLECT_OPEN_ACTION_MODULE_ALLOWLISTED
This emits when an open action module is allowlisted or removed from allowlist.
interface Response {
moduleName: string;
collectModule: string;
whitelist: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
CURRENCY_MODULE_ALLOWLISTED
This emits when a currency module is allowlisted or removed from allowlist.
interface Response {
currency: string;
name: string;
symbol: string;
decimals: number;
prevWhitelisted: boolean;
whitelisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"currency": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
"name": "Wrapped Matic",
"symbol": "WMATIC",
"decimals": 18,
"prevWhitelisted": false,
"whitelisted": true,
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
FOLLOW_MODULE_ALLOWLISTED
This emits when a follow module is allowlisted or removed from allowlist.
interface Response {
followModule: string;
whitelisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"followModule": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
"whitelisted": true,
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
FOLLOW_MODULE_SET
This emits when a profile changes their follow module.
interface Response {
profileId: string;
followModule: string;
followModuleReturnData: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"profileId": "0x01",
"followModule": "0xe7AB9BA11b97EAC820DbCc861869092b52B65C06",
"followModuleReturnData": "0x00000000000000000000000000000000000000000000000000000000000027100000000000000000000000002058a9d7613eee744279e3856ef0eada5fcbaa7e0000000000000000000000001d441b02fa5a2b609a83ccb5df1bfeba91a0794e",
"timestamp": 1656073676000,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
REFERENCE_MODULE_ALLOWLISTED
This emits when a reference module is allowlisted or unallowlisted.
interface Response {
referenceModule: string;
referenceModuleName: string;
whitelisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"referenceModule": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"referenceModuleName": "FollowerOnlyReferenceModule",
"allowlisted": true,
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
OPEN_ACTION_MODULE_ALLOWLISTED
This emits when a new open action module is allowlisted or unallowlisted
interface Response {
actionModule: string;
openActionModuleName: string;
id: string;
whitelisted: boolean;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROTOCOL_LENS_HUB_ADDRESS_SIG_NONCE_UPDATED
This emits when a lens hub address signature nonce is updated.
interface Response {
signer: string;
nonce: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROTOCOL_TOKEN_HANDLE_ADDRESS_SIG_NONCE_UPDATED
This emits when a token handle address signature nonce is updated.
interface Response {
signer: string;
nonce: string;
timestamp: number;
logIndex: number;
txHash: string;
txIndex: number;
}
PROTOCOL_STATE_CHANGED
This emits when the state of the protocol is changed.
0: Unpaused
1: PublishingPaused
2: Paused
interface Response {
caller: string;
prevState: number;
newState: number;
logIndex: number;
txHash: string;
txIndex: number;
}
Response example
{
"caller": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
"prevState": 2,
"newState": 0,
"allowlisted": true,
"timestamp": 1656073676000,
"logIndex": 1,
"txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
"txIndex": 8
}
Media related
MEDIA_SNAPSHOTTED
This emits when a media is successfully snapshotted. This can be triggered when
a publication with media gets posted, or when a profile updates their metadata.
In the former, the source
will be the PublicationId and on the latter it will
be the ProfileId.
interface Response {
originalUrl: string;
snapshottedUrl: string;
source: string;
}
Response example
{
"originalUrl": "https://my-media.com/image.png",
"snapshottedUrl": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/media/0x01-0x02.png",
"source": "0x01-0x02"
}
MEDIA_SNAPSHOT_FAILED
This emits when a media fails to be snapshotted. This can happen due to timing
out after all retry attempts, due to url being invalid or content unavailable
at the time (404 or IPFS pinning unfinished).
interface Response {
originalUrl: string;
source: string;
message: string;
}
Response example
{
"originalUrl": "https://my-media.com/image.png",
"source": "0x01-0x02",
"message": "Snapshot failed: Failed to fetch"
}
Updated 18 days ago