Bundlr + Lens Protocol
Lens Protocol is a composable and decentralized social graph; it allows you to quickly create social applications without having to build your own large backend services. The Lens documentation is extensive and very well done. The point of this guide isn't to replicate anything on the Lens site, rather it's a focused guide showing how to create and store your post metadata on Arweave via Bundlr. For additional information of creating a post on Lens, definitely spend some time on their docs..
When working with Lens, you upload structured metadata to a permanent storage provider (Bundlr) and then pass the URL to that content to Lens.
There are two ways to interact with Lens:
React Hooks
The React hooks for Lens make client-side development easier as streamline much of the development, including generating correct metadata for you to upload.
upload()
pattern
When working with hooks like useUpdateProfileDetails()
and useCreatePost()
, Lens works using a pattern where you define a function called upload()
that matches the following signature.
export const upload = (data: unknown): Promise<string> => {
const serialized = JSON.stringify(data);
const url = // upload serialized to a public location
return url;
}
The upload()
function is passed to the hooks on initialization, the hooks then prepare the required metadata and pass it to upload()
in the data
parameter. In implementing the function, it's up to you to serialize the metadata (JSON object), store it on Bundlr and then return the URL from the function. The hook then takes the URL to the uploaded metadata and continues using it to update profile details or create a new post.
A full ready-to-use implementation of the upload()
function is as follows:
import { WebBundlr } from "@bundlr-network/client";
import { getBundlr } from "./get-bundlr";
import { fetchSigner } from "wagmi/actions";
const getBundlr = async () => {
const signer = await fetchSigner();
const provider = signer?.provider;
const bundlr = new WebBundlr("https://node2.bundlr.network", "matic", provider);
await bundlr.ready();
return bundlr;
};
export const upload = async (data) => {
try {
const bundlr = await getBundlr();
const serialized = JSON.stringify(data);
// fund (if needed)
const price = await bundlr.getPrice(new Blob([serialized]).size);
await bundlr.fund(price);
const tx = await bundlr.upload(serialized, {
tags: [{ name: "Content-Type", value: "application/json" }],
});
console.log(`Upload success content URL= https://arweave.net/${tx.id}`);
return `https://arweave.net/${tx.id}`;
} catch (e) {
console.log("error on upload ", e);
}
return "";
};
If you incorrectly set the Content-Type
attribute, Lens will not read your data.
Using post()
and postWithSig()
When working with the functions post()
and postWithSig()
, it is your responsibility to correctly build the required metadata for each interaction, store it on Bundlr and then pass the URL to Lens.
Uploading Metadata
The full Lens Protocol metadata specification details how to create just about any type of post. For this guide, we're focusing on an image post and will use the following metadata.
{
"version": "2.0.0",
"metadata_id": "${uuid()}",
"description": "gm (🌿, 🌿)",
"image": "https://arweave.net/CO9EpX0lekJEfXUOeXncUmMuG8eEp5WJHXl9U9yZUYA",
"imageMimeType": "image/png",
"name": "Post by foo.lens",
"attributes": [{ "traitType": "type", "value": "POST" }],
"media": [
{
"item": "https://arweave.net/CO9EpX0lekJEfXUOeXncUmMuG8eEp5WJHXl9U9yZUYA",
"type": "image/png",
"altTag": ""
}
],
"appId": "MyApp",
"locale": "en",
"mainContentFocus": "IMAGE"
}
The function uuid()
generates a unique post id using the UUID npm package.
To upload to Bundlr, first stringify the data, then pass it to bundlr.upload()
, tagging it with the Content-Type
of application/json
.
// Create a WebBundlr object
const bundlr = new WebBundlr("https://node2.bundlr.network", "matic", provider);
await bundlr.ready();
// Stringify our data
const data = JSON.stringify(metadata);
// Upload to Arweave via Bundlr
const tx = await bundlr.upload(data, {
tags: [{ name: "Content-Type", value: "application/json" }],
});
// Print our URI, note use of HTTPS
console.log(`Upload success content URI= https://arweave.net/${tx.id}`);
If you incorrectly set the Content-Type
attribute, Lens will not read your data.
Creating A Post
After successfully uploading your metadata, you'll get a transaction id that can be combined with https://arweave.net/
to create your content URI (https://arweave.net/6eARMSxeRRhOU0iJEivNB_naY-YFuh_D9d0-58ccKfI
).
Finally, create a post request using the generated content URL and pass to the Lens contract calling either of the .post()
or .postWithSig()
functions.
const tx = await lensHub.postWithSig({
profileId: typedData.value.profileId,
contentURI: typedData.value.contentURI, // URL from the metadata uploaded above
collectModule: typedData.value.collectModule,
collectModuleInitData: typedData.value.collectModuleInitData,
referenceModule: typedData.value.referenceModule,
referenceModuleInitData: typedData.value.referenceModuleInitData,
sig: {
v,
r,
s,
deadline: typedData.value.deadline,
},
});
While http://arweave.net/[transaction-id]
and https://arweave.net/[transaction-id]
will both work in your browser, only the https
version will work when creating your Lens post.