Skip to content

Commit d50b3a1

Browse files
authored
Merge pull request #4 from polkadot-api/vo-gov-sdk
feat(referenda-sdk): watch referenda
2 parents 3e1436a + 54a6856 commit d50b3a1

File tree

6 files changed

+144
-78
lines changed

6 files changed

+144
-78
lines changed

packages/sdk-governance/src/bounties/bounties-sdk.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
import { combineKeys, partitionByKey, toKeySet } from "@react-rxjs/utils"
1+
import { partitionEntries } from "@/util/watchEntries"
2+
import { combineKeys, toKeySet } from "@react-rxjs/utils"
23
import { Binary, TxEvent } from "polkadot-api"
3-
import {
4-
combineLatest,
5-
distinctUntilChanged,
6-
map,
7-
mergeMap,
8-
takeWhile,
9-
} from "rxjs"
4+
import { combineLatest, distinctUntilChanged, map } from "rxjs"
105
import { getBountyDescriptions$ } from "./bounty-descriptions"
116
import { BountiesSdkTypedApi, BountyWithoutDescription } from "./descriptors"
127
import {
@@ -138,32 +133,8 @@ export function createBountiesSdk(typedApi: BountiesSdkTypedApi): BountiesSdk {
138133
}
139134

140135
function watchBounties() {
141-
const [getBountyById$, bountyKeyChanges$] = partitionByKey(
142-
typedApi.query.Bounties.Bounties.watchEntries().pipe(
143-
mergeMap((v) =>
144-
v.deltas
145-
? [
146-
...v.deltas.deleted.map((d) => ({
147-
id: d.args[0],
148-
value: undefined,
149-
})),
150-
...v.deltas.upserted.map((d) => ({
151-
id: d.args[0],
152-
value: d.value,
153-
})),
154-
].sort((a, b) => a.id - b.id)
155-
: [],
156-
),
157-
),
158-
(res) => res.id,
159-
(group$, id) =>
160-
group$.pipe(
161-
takeWhile(({ value }) => Boolean(value), false),
162-
map((bounty) => ({
163-
id,
164-
bounty: bounty.value!,
165-
})),
166-
),
136+
const [getBountyById$, bountyKeyChanges$] = partitionEntries(
137+
typedApi.query.Bounties.Bounties.watchEntries(),
167138
)
168139
const descriptions$ = getBountyDescriptions$(
169140
typedApi.query.Bounties.BountyDescriptions.getEntries,
@@ -184,13 +155,11 @@ export function createBountiesSdk(typedApi: BountiesSdkTypedApi): BountiesSdk {
184155
distinctUntilChanged(),
185156
),
186157
]).pipe(
187-
map(([{ id, bounty }, description]) =>
188-
enhanceBounty(bounty, description, id),
189-
),
158+
map(([bounty, description]) => enhanceBounty(bounty, description, id)),
190159
)
191160

192161
return {
193-
bounties$: combineKeys(bountyIds$, getEnhancedBountyById$),
162+
bounties$: combineKeys(bountyKeyChanges$, getEnhancedBountyById$),
194163
getBountyById$: getEnhancedBountyById$,
195164
bountyIds$,
196165
}

packages/sdk-governance/src/bounties/child-bounties-sdk.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import { combineKeys, partitionByKey, toKeySet } from "@react-rxjs/utils"
2-
import {
3-
combineLatest,
4-
distinctUntilChanged,
5-
map,
6-
mergeMap,
7-
takeWhile,
8-
} from "rxjs"
1+
import { keyedMemo } from "@/util/memo"
2+
import { partitionEntries } from "@/util/watchEntries"
3+
import { combineKeys, toKeySet } from "@react-rxjs/utils"
4+
import { combineLatest, distinctUntilChanged, map } from "rxjs"
95
import { getBountyDescriptions$ } from "./bounty-descriptions"
106
import {
117
ChildBountiesSdkTypedApi,
@@ -16,7 +12,6 @@ import {
1612
ChildBounty,
1713
GenericChildBounty,
1814
} from "./child-sdk-types"
19-
import { keyedMemo } from "@/util/memo"
2015

2116
export function createChildBountiesSdk(
2217
typedApi: ChildBountiesSdkTypedApi,
@@ -111,32 +106,8 @@ export function createChildBountiesSdk(
111106
}
112107

113108
function watchChildBounties(parentId: number) {
114-
const [getBountyById$, bountyKeyChanges$] = partitionByKey(
115-
typedApi.query.ChildBounties.ChildBounties.watchEntries(parentId).pipe(
116-
mergeMap((v) =>
117-
v.deltas
118-
? [
119-
...v.deltas.deleted.map((d) => ({
120-
id: d.args[1],
121-
value: undefined,
122-
})),
123-
...v.deltas.upserted.map((d) => ({
124-
id: d.args[1],
125-
value: d.value,
126-
})),
127-
].sort((a, b) => a.id - b.id)
128-
: [],
129-
),
130-
),
131-
(res) => res.id,
132-
(group$, id) =>
133-
group$.pipe(
134-
takeWhile(({ value }) => Boolean(value), false),
135-
map((bounty) => ({
136-
id,
137-
bounty: bounty.value!,
138-
})),
139-
),
109+
const [getBountyById$, bountyKeyChanges$] = partitionEntries(
110+
typedApi.query.ChildBounties.ChildBounties.watchEntries(parentId),
140111
)
141112
const descriptions$ = getBountyDescriptions$(
142113
typedApi.query.ChildBounties.ChildBountyDescriptions.getEntries,
@@ -157,9 +128,7 @@ export function createChildBountiesSdk(
157128
distinctUntilChanged(),
158129
),
159130
]).pipe(
160-
map(([{ id, bounty }, description]) =>
161-
enhanceBounty(bounty, description, id),
162-
),
131+
map(([bounty, description]) => enhanceBounty(bounty, description, id)),
163132
)
164133

165134
return {

packages/sdk-governance/src/referenda/referenda-sdk.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { Deltas, partitionEntries } from "@/util/watchEntries"
12
import { blake2b } from "@noble/hashes/blake2b"
3+
import { combineKeys, toKeySet } from "@react-rxjs/utils"
24
import { Binary, TxEvent } from "polkadot-api"
5+
import { map, scan } from "rxjs"
36
import { getPreimageResolver } from "../preimages"
47
import { originToTrack, polkadotSpenderOrigin } from "./chainConfig"
58
import {
@@ -133,6 +136,70 @@ export function createReferendaSdk(
133136
})
134137
.filter((v) => !!v)
135138
}
139+
async function getOngoingReferendum(id: number) {
140+
const referendum =
141+
await typedApi.query.Referenda.ReferendumInfoFor.getValue(id)
142+
if (referendum?.type === "Ongoing") {
143+
return enhanceOngoingReferendum(id, referendum.value)
144+
}
145+
return null
146+
}
147+
148+
const [rawReferendumById$, referendaKeyChange$] = partitionEntries(
149+
typedApi.query.Referenda.ReferendumInfoFor.watchEntries().pipe(
150+
scan(
151+
(acc, v) => {
152+
if (!v.deltas) return { ...acc, deltas: null }
153+
const deleted = v.deltas.deleted.map((v) => ({
154+
...v,
155+
value: v.value.value as RawOngoingReferendum,
156+
}))
157+
const upserted = v.deltas.upserted
158+
.map((v) => {
159+
if (v.value.type === "Ongoing") {
160+
acc.referendums[v.args[0]] = v.value.value
161+
return {
162+
args: v.args,
163+
value: v.value.value,
164+
}
165+
}
166+
if (v.args[0] in acc.referendums) {
167+
// An Ongoing has become closed, remove from list
168+
deleted.push({
169+
args: v.args,
170+
value: acc.referendums[v.args[0]],
171+
})
172+
delete acc.referendums[v.args[0]]
173+
}
174+
return null!
175+
})
176+
.filter(Boolean)
177+
178+
return {
179+
referendums: acc.referendums,
180+
deltas: { deleted, upserted },
181+
}
182+
},
183+
{
184+
referendums: {} as Record<number, RawOngoingReferendum>,
185+
deltas: null as Deltas<RawOngoingReferendum> | null,
186+
},
187+
),
188+
),
189+
)
190+
191+
const getOngoingReferendumById$ = (id: number) =>
192+
rawReferendumById$(id).pipe(
193+
map((entry) => enhanceOngoingReferendum(id, entry)),
194+
)
195+
const ongoingReferenda$ = combineKeys(
196+
referendaKeyChange$,
197+
getOngoingReferendumById$,
198+
)
199+
const ongoingReferendaIds$ = referendaKeyChange$.pipe(
200+
toKeySet(),
201+
map((set) => [...set]),
202+
)
136203

137204
const getSpenderTrack: ReferendaSdk["getSpenderTrack"] = (value) => {
138205
const spenderOriginType = spenderOrigin(value)
@@ -223,7 +290,13 @@ export function createReferendaSdk(
223290
: null
224291

225292
return {
293+
watch: {
294+
ongoingReferenda$,
295+
getOngoingReferendumById$,
296+
ongoingReferendaIds$,
297+
},
226298
getOngoingReferenda,
299+
getOngoingReferendum,
227300
getSpenderTrack,
228301
getTrack,
229302
createReferenda,

packages/sdk-governance/src/referenda/sdk-types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ReferendumInfo,
99
TraitsScheduleDispatchTime,
1010
} from "./descriptors"
11+
import { Observable } from "rxjs"
1112

1213
type RawOngoingReferendum = (ReferendumInfo & { type: "Ongoing" })["value"]
1314

@@ -60,6 +61,12 @@ export type ReferendaTrack = Omit<
6061

6162
export interface ReferendaSdk {
6263
getOngoingReferenda(): Promise<OngoingReferendum[]>
64+
getOngoingReferendum(id: number): Promise<OngoingReferendum | null>
65+
watch: {
66+
ongoingReferenda$: Observable<Map<number, OngoingReferendum>>
67+
ongoingReferendaIds$: Observable<number[]>
68+
getOngoingReferendumById$: (key: number) => Observable<OngoingReferendum>
69+
}
6370
getSpenderTrack(value: bigint): {
6471
origin: PolkadotRuntimeOriginCaller
6572
track: Promise<ReferendaTrack>

packages/sdk-governance/src/referenda/track.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function trackFetcher(typedApi: ReferendaSdkTypedApi) {
4141
}
4242

4343
const BILLION = 1_000_000_000_000
44+
const BIG_BILLION = 1_000_000_000_000n
4445
const blockToPerBill = (block: number, period: number) =>
4546
(block * BILLION) / period
4647
const perBillToBlock = (perBillion: number, period: number) =>
@@ -181,10 +182,14 @@ function reciprocal({
181182
// Below horizontal asymptote => +Infinity
182183
if (bigValue <= -y_offset) return Number.POSITIVE_INFINITY
183184
// Above y-axis cut => -Infinity
184-
if (x_offset != 0n && bigValue > factor / x_offset - y_offset)
185+
// It needs to be multiplied by BIG_BILLION when dividing because we're working with perbillion
186+
if (
187+
x_offset != 0n &&
188+
bigValue > (BIG_BILLION * factor) / x_offset - y_offset
189+
)
185190
return Number.NEGATIVE_INFINITY
186191

187-
return Number(factor / (bigValue + y_offset) - x_offset)
192+
return Number((BIG_BILLION * factor) / (bigValue + y_offset) - x_offset)
188193
}
189194
const getData = (step: number) => {
190195
const result: Array<{
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { partitionByKey } from "@react-rxjs/utils"
2+
import { map, mergeMap, Observable, takeWhile } from "rxjs"
3+
4+
export type Deltas<T> = {
5+
deleted: {
6+
args: Array<number>
7+
value: T
8+
}[]
9+
upserted: {
10+
args: Array<number>
11+
value: T
12+
}[]
13+
}
14+
export function partitionEntries<T>(
15+
entries$: Observable<{
16+
deltas: Deltas<T> | null
17+
}>,
18+
) {
19+
return partitionByKey(
20+
entries$.pipe(
21+
mergeMap((v) =>
22+
v.deltas
23+
? [
24+
...v.deltas.deleted.map((d) => ({
25+
id: d.args.at(-1)!,
26+
value: undefined,
27+
})),
28+
...v.deltas.upserted.map((d) => ({
29+
id: d.args.at(-1)!,
30+
value: d.value,
31+
})),
32+
].sort((a, b) => a.id - b.id)
33+
: [],
34+
),
35+
),
36+
(res) => res.id,
37+
(group$) =>
38+
group$.pipe(
39+
takeWhile(({ value }) => Boolean(value), false),
40+
map(({ value }) => value!),
41+
),
42+
)
43+
}

0 commit comments

Comments
 (0)