Skip to content

Commit 0782ebe

Browse files
authored
Reinstate NIP-88: Polls on Nostr (#1507)
1 parent 512caf7 commit 0782ebe

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

88.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# NIP-88
2+
3+
## Polls
4+
5+
`draft` `optional`
6+
7+
This NIP defines the event scheme that describe Polls on nostr.
8+
9+
## Events
10+
11+
### Poll Event
12+
13+
The poll event is defined as a `kind:1068` event.
14+
15+
- **content** key holds the label for the poll.
16+
17+
Major tags in the poll event are:
18+
19+
- **option**: The option tags contain an OptionId(any alphanumeric) field, followed by an option label field.
20+
- **relay**: One or multiple tags that the poll is expecting respondents to respond on.
21+
- **polltype**: can be "singlechoice" or "multiplechoice". Polls that do not have a polltype should be considered a "singlechoice" poll.
22+
- **endsAt**: signifying at which unix timestamp the poll is meant to end.
23+
24+
Example Event
25+
26+
```json
27+
{
28+
"content": "Pineapple on pizza",
29+
"created_at": 1719888496,
30+
"id": "9d1b6b9562e66f2ecf35eb0a3c2decc736c47fddb13d6fb8f87185a153ea3634",
31+
"kind": 1068,
32+
"pubkey": "dee45a23c4f1d93f3a2043650c5081e4ac14a778e0acbef03de3768e4f81ac7b",
33+
"sig": "7fa93bf3c430eaef784b0dacc217d3cd5eff1c520e7ef5d961381bc0f014dde6286618048d924808e54d1be03f2f2c2f0f8b5c9c2082a4480caf45a565ca9797",
34+
"tags": [
35+
["option", "qj518h583", "Yay"],
36+
["option", "gga6cdnqj", "Nay"],
37+
["relay", "<relay url1>"],
38+
["relay", "<relay url2>"],
39+
["polltype", "singlechoice"],
40+
["endsAt", "<unix timestamp in seconds>"]
41+
]
42+
}
43+
```
44+
45+
### Responses
46+
47+
The response event is a `kind:1018` event. It contains an e tag with the poll event it is referencing, followed by one or more response tags.
48+
49+
- **response** : The tag contains "response" as it's first positional argument followed by the option Id selected.
50+
51+
The responses are meant to be published to the relays specified in the poll event.
52+
53+
Example Response Event
54+
55+
```json
56+
{
57+
"content": "",
58+
"created_at": 1720097117,
59+
"id": "60a005e32e9596c3f544a841a9bc4e46d3020ca3650d6a739c95c1568e33f6d8",
60+
"kind": 1018,
61+
"pubkey": "1bc70a0148b3f316da33fe7e89f23e3e71ac4ff998027ec712b905cd24f6a411",
62+
"sig": "30071a633c65db8f3a075c7a8de757fbd8ce65e3607f4ba287fe6d7fbf839a380f94ff4e826fbba593f6faaa13683b7ea9114ade140720ecf4927010ebf3e44f",
63+
"tags": [
64+
["e", "1fc80cf813f1af33d5a435862b7ef7fb96b47e68a48f1abcadf8081f5a545550"],
65+
["response", "gga6cdnqj"],
66+
["response", "m3agjsdq1"]
67+
]
68+
}
69+
```
70+
71+
### Poll Types
72+
73+
The polltype setting dictates how multiple response tags are handled in the `kind:1018` event.
74+
75+
- **polltype: singlechoice**: The first response tag is to be considered the actual response.
76+
- **polltype: multiplechoice**: The first response tag pointing to each id is considered the actual response, without considering the order of the response tags.
77+
78+
### Counting Results
79+
80+
Results can be queried by fetching `kind:1018` events from the relays specified in the poll.
81+
The results displayed should only be 1 vote event per pubkey.
82+
In case of multiple events for a pubkey, the event with the largest timestamp within the poll limits should be considered.
83+
84+
Example for querying polls.
85+
86+
```ts
87+
const fetchVoteEvents = (filterPubkeys: string[]) => {
88+
let resultFilter: Filter = {
89+
"#e": [pollEvent.id],
90+
kinds: [1018],
91+
};
92+
if (filterPubkeys?.length) {
93+
resultFilter.authors = filterPubkeys;
94+
}
95+
if (pollExpiration) {
96+
resultFilter.until = Number(pollExpiration);
97+
}
98+
pool.subscribeMany(relays, [resultFilter], {
99+
onevent: handleResultEvent,
100+
});
101+
};
102+
```
103+
104+
Example for maintaining OneVotePerPubkey
105+
106+
```ts
107+
const oneVotePerPubkey = (events: Event[]) => {
108+
const eventMap = new Map<string, Event>();
109+
110+
events.forEach((event) => {
111+
if (
112+
!eventMap.has(event.pubkey) ||
113+
event.created_at > eventMap.get(event.pubkey)!.created_at
114+
) {
115+
eventMap.set(event.pubkey, event);
116+
}
117+
});
118+
119+
return Array.from(eventMap.values());
120+
};
121+
```
122+
123+
### Relays
124+
125+
It is advisable for poll authors to use relays that do not allow backdated events and do not honor kind:5 (delete) requests for vote events in order to maintain the integrity of poll results after the poll has ended.
126+
127+
### Curation
128+
129+
The clients may configure fetching results by specific people. This can be achieved by creating `kind:30000` follow sets, and fetching results only from the follow set.
130+
Clients can also employ other curation algorithms, like Proof Of Work and Web of Trust scores for result curations.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
8989
- [NIP-78: Application-specific data](78.md)
9090
- [NIP-84: Highlights](84.md)
9191
- [NIP-86: Relay Management API](86.md)
92+
- [NIP-88: Polls](88.md)
9293
- [NIP-89: Recommended Application Handlers](89.md)
9394
- [NIP-90: Data Vending Machines](90.md)
9495
- [NIP-92: Media Attachments](92.md)
@@ -128,11 +129,13 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
128129
| `44` | Channel Mute User | [28](28.md) |
129130
| `64` | Chess (PGN) | [64](64.md) |
130131
| `818` | Merge Requests | [54](54.md) |
132+
| `1018` | Poll Response | [88](88.md) |
131133
| `1021` | Bid | [15](15.md) |
132134
| `1022` | Bid confirmation | [15](15.md) |
133135
| `1040` | OpenTimestamps | [03](03.md) |
134136
| `1059` | Gift Wrap | [59](59.md) |
135137
| `1063` | File Metadata | [94](94.md) |
138+
| `1068` | Poll | [88](88.md) |
136139
| `1111` | Comment | [22](22.md) |
137140
| `1311` | Live Chat Message | [53](53.md) |
138141
| `1617` | Patches | [34](34.md) |

0 commit comments

Comments
 (0)