Snapshot integration
Integrate Snapshot voting into your Lens app
With version 1.2 of the Lens SDK we introduced an integration with Snapshot voting system. In this guide we will show you how to add, visualize, and vote for a Snapshot Proposal from your Lens app.
This guide will assume you have familiarity with Snapshot. Refer to their documentation for more details.
Add Snapshot proposal to a publication
We will assume you know how to create a Snapshot Proposal. Just paste the Proposal URL into the content
of a Post or Comment and publish it.
import { ContentFocus, ProfileOwnedByMe, useCreatePost } from '@lens-protocol/react-web';
import { uploadJson } from './upload'
function Composer({ publisher }: { publisher: ProfileOwnedByMe }) {
const { execute: create, error, isPending } = useCreatePost({ publisher, upload: uploadJson });
const onSubmit = async (content: string) => {
await create({
content: `
Vote my proposal here:
https://snapshot.org/#/aave.eth/proposal/0xb17b3294dcb08316cb623c717add7f82df54948d558992f886be59d0958e9b24
`,
contentFocus: ContentFocus.TEXT,
locale: 'en',
});
};
// ...
}
See create your first post guide for more details on how to create a post.
Demo Snapshot
Use real Snapshot Proposal URLs when using Lens
production
environment and https://demo.snapshot.org Proposals when usingdevelopment
orsandbox
environments.
Visualize Snapshot Proposal details
When rendering a Publication
you can check if it contains a Snapshot Proposal and extract its details for rendering a Twitter-like polls UI:

import { isPollPublication, PollPublication, Publication, usePollDetails } from '@lens-protocol/react-web';
function Poll({ publication }: { publication: PollPublication }) {
const { data, error, loading } = usePollDetails({ publication });
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>{error.message}</p>;
}
return (
<div>
<h2>{data.title} ({data.isActive ? 'active' : 'closed'})</h2>
<ul>
{data.choices.map((choice, idx) => (
<li key={idx}>
{choice.label} ({choice.percentage.toPrecision(3)}%) - {choice.didVote ? '✅' : null}
</li>
))}
</ul>
</div>
);
}
export function PublicationContent({ publication }: { publication: Publication }) {
if (isPollPublication(publication)) {
return <Poll publication={
// render other publications as usual
return (
);
}
What's happening?
- we use the
isPollPublication
to determine if the publication is of typePollPublication
and render the publication with a dedicated<Poll>
component.PollPublication
is a special type ofPost | Comment
that contains opaque metadata used by the SDK to fetch data and, later on, vote on a given poll. - we use the
usePollDetails
to fetch details about the Snapshot Proposal.
In the specific the data
have the following details:
type SnapshotDetails = {
author: EthereumAddress;
choices: TwoAtLeastArray<PollChoice>;
eligibility: VoteEligibility; // CAN_VOTE | CANNOT_VOTE | UNKNOWN
endAt: Date;
isActive: boolean;
isSingleChoice: boolean;
quorum: number;
snapshot: string | null;
space: SnapshotSpace;
startAt: Date;
system: SnapshotVotingSystem;
title: string;
totalVotes: number;
url: Url;
};
Each item in choices
has the following shape:
type PollChoice = {
label: string;
votes: number; // the total numbers of votes
didVote: boolean; // a boolean representing if the logged-in wallet has voted or not
percentage: number; // a decimal number between 0 and 100
};
The error
could be of type NotFoundError
indicating the given Snapshot Proposal no longer exist (or the URL was malformed and lead to a not existent proposal ID).
See Lens SDK reference document for more details on these types.
Vote a Snapshot Proposal
In the following we will focus on the PollPublication
component from the previous example by changing the UI into a form and adding voting capability to it.
import { PollPublication, Publication, usePollDetails, usePollVote } from '@lens-protocol/react-web';
function Poll({ publication }: { publication: PollPublication }) {
const { data } = usePollDetails({ publication });
const { execute, error, isPending } = usePollVote({ publication });
// omitted loading and fetching error for brevity
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const form = event.currentTarget;
const formData = new FormData(form);
const choice = parseInt((formData.get('choice') as string) ?? never());
await execute(choice);
};
return (
<form onSubmit={onSubmit}>
<fieldset>
<legend>{data.title} ({data.isActive ? 'active' : 'closed'})</legend>
{data.choices.map((choice, idx) => (
<label key={idx}>
<input
disabled={!poll.isActive}
type={data.isSingleChoice ? 'radio' : 'checkbox'}
defaultChecked={data.didVote}
name="choice"
value={idx}
/>
{choice.label} ({choice.percentage.toPrecision(3)}%)
</label>
))}
<button disabled={!data.isActive || isPending} type="submit">
Vote
</button>
{error && <pre>{error.message}</pre>}
</fieldset>
</form>
);
}
What's happening?
- we wired a second React hook called
usePollVote
into the previous example UI - we render the polls choices as mutually exclusive (radio buttons) or not (checkboxes) according to
data.isSingleChoice
flag. - we use a
<form>
to let the user express their choice and submit it - we vote by calling
execute
callback with the user's choice(s).
Updated 3 months ago