Skip to content

Commit b62fb19

Browse files
committed
feat: add orchestrator reward & fee cut history charts
Delegators can now see how an orchestrator has changed their reward and fee cuts over time before delegating. - New OrchestratorCutHistory component renders two step-line charts (reward cut and fee cut) via ExplorerChart, placed above the stats masonry on the orchestrator page - New useOrchestratorCutHistory hook fetches historical TranscoderUpdateEvents from the subgraph and prepares chart-ready data, with two anchor points for edge cases: - a "now" anchor so the line extends to the present day when the last on-chain update is older than 24h - an "activation" anchor synthesized from the current on-chain cut values at `activationTimestamp` when the orchestrator has no TranscoderUpdateEvents at all (activated with cuts they've never changed), so the chart still renders a flat line from activation → now instead of hanging on a loading skeleton - Chart is hidden when there's nothing to plot (never-activated accounts) rather than showing an empty panel - ExplorerChart gains a lineCurve prop (monotone / stepAfter / linear) so cut charts render with the correct step-style line - ExplorerChart tooltip widened to ReactNode; the hardcoded estimation methodology note is removed from the default and passed inline from the Estimated Usage chart (the only chart it applies to, per the forum post)
1 parent 2a5d393 commit b62fb19

7 files changed

Lines changed: 394 additions & 133 deletions

File tree

apollo/subgraph.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10843,6 +10843,50 @@ export function useTranscoderActivatedEventsLazyQuery(baseOptions?: Apollo.LazyQ
1084310843
export type TranscoderActivatedEventsQueryHookResult = ReturnType<typeof useTranscoderActivatedEventsQuery>;
1084410844
export type TranscoderActivatedEventsLazyQueryHookResult = ReturnType<typeof useTranscoderActivatedEventsLazyQuery>;
1084510845
export type TranscoderActivatedEventsQueryResult = Apollo.QueryResult<TranscoderActivatedEventsQuery, TranscoderActivatedEventsQueryVariables>;
10846+
10847+
export type TranscoderUpdateEventsQueryVariables = Exact<{
10848+
where?: InputMaybe<TranscoderUpdateEvent_Filter>;
10849+
first?: InputMaybe<Scalars['Int']>;
10850+
orderBy?: InputMaybe<TranscoderUpdateEvent_OrderBy>;
10851+
orderDirection?: InputMaybe<OrderDirection>;
10852+
}>;
10853+
10854+
export type TranscoderUpdateEventsQuery = { __typename: 'Query', transcoderUpdateEvents: Array<{ __typename: 'TranscoderUpdateEvent', id: string, rewardCut: string, feeShare: string, timestamp: number, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string } }> };
10855+
10856+
export const TranscoderUpdateEventsDocument = gql`
10857+
query transcoderUpdateEvents($where: TranscoderUpdateEvent_filter, $first: Int, $orderBy: TranscoderUpdateEvent_orderBy, $orderDirection: OrderDirection) {
10858+
transcoderUpdateEvents(
10859+
where: $where
10860+
first: $first
10861+
orderBy: $orderBy
10862+
orderDirection: $orderDirection
10863+
) {
10864+
id
10865+
rewardCut
10866+
feeShare
10867+
timestamp
10868+
round {
10869+
id
10870+
}
10871+
transaction {
10872+
id
10873+
}
10874+
}
10875+
}
10876+
`;
10877+
10878+
export function useTranscoderUpdateEventsQuery(baseOptions?: Apollo.QueryHookOptions<TranscoderUpdateEventsQuery, TranscoderUpdateEventsQueryVariables>) {
10879+
const options = {...defaultOptions, ...baseOptions}
10880+
return Apollo.useQuery<TranscoderUpdateEventsQuery, TranscoderUpdateEventsQueryVariables>(TranscoderUpdateEventsDocument, options);
10881+
}
10882+
export function useTranscoderUpdateEventsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TranscoderUpdateEventsQuery, TranscoderUpdateEventsQueryVariables>) {
10883+
const options = {...defaultOptions, ...baseOptions}
10884+
return Apollo.useLazyQuery<TranscoderUpdateEventsQuery, TranscoderUpdateEventsQueryVariables>(TranscoderUpdateEventsDocument, options);
10885+
}
10886+
export type TranscoderUpdateEventsQueryHookResult = ReturnType<typeof useTranscoderUpdateEventsQuery>;
10887+
export type TranscoderUpdateEventsLazyQueryHookResult = ReturnType<typeof useTranscoderUpdateEventsLazyQuery>;
10888+
export type TranscoderUpdateEventsQueryResult = Apollo.QueryResult<TranscoderUpdateEventsQuery, TranscoderUpdateEventsQueryVariables>;
10889+
1084610890
export const TreasuryProposalDocument = gql`
1084710891
query treasuryProposal($id: ID!) {
1084810892
treasuryProposal(id: $id) {

components/ExplorerChart/index.tsx

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ const ExplorerChart = ({
6363
basePercentChange,
6464
unit = "none",
6565
type,
66+
lineCurve = "monotone",
6667
grouping = "day",
6768
onToggleGrouping,
6869
}: {
6970
title: string;
70-
tooltip: string;
71+
tooltip: React.ReactNode;
7172
base: number;
7273
basePercentChange: number;
7374
data: ChartDatum[];
@@ -80,6 +81,7 @@ const ExplorerChart = ({
8081
| "small-unitless"
8182
| "none";
8283
type: "bar" | "line";
84+
lineCurve?: "monotone" | "stepAfter" | "linear";
8385
grouping?: Group;
8486
onToggleGrouping?: (grouping: Group) => void;
8587
}) => {
@@ -223,7 +225,7 @@ const ExplorerChart = ({
223225
unit === "small-percent"
224226
? 45
225227
: unit === "percent"
226-
? 35
228+
? 42
227229
: unit === "minutes"
228230
? 35
229231
: unit === "eth"
@@ -245,23 +247,7 @@ const ExplorerChart = ({
245247
<ExplorerTooltip
246248
multiline
247249
side="bottom"
248-
content={
249-
tooltip ? (
250-
<>
251-
<div>{tooltip}</div>
252-
<br />
253-
<div>
254-
{`The estimation methodology was updated on 8/21/23. `}
255-
<a href="https://forum.livepeer.org/t/livepeer-explorer-minutes-estimation-methodology/2140">
256-
Read more about the changes
257-
</a>
258-
{"."}
259-
</div>
260-
</>
261-
) : (
262-
<></>
263-
)
264-
}
250+
content={tooltip ? <div>{tooltip}</div> : <></>}
265251
>
266252
<Flex
267253
css={{
@@ -485,7 +471,7 @@ const ExplorerChart = ({
485471

486472
<Line
487473
dataKey="y"
488-
type="monotone"
474+
type={lineCurve}
489475
dot={{ r: 0, strokeWidth: 0 }}
490476
activeDot={{ r: 3, strokeWidth: 0 }}
491477
stroke="rgba(0, 235, 136, 0.8)"

components/OrchestratingView/index.tsx

Lines changed: 109 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import OrchestratorCutHistory from "@components/OrchestratorCutHistory";
12
import Stat from "@components/Stat";
23
import dayjs from "@lib/dayjs";
34
import { Box, Flex, Link as A, Text } from "@livepeer/design-system";
@@ -248,21 +249,6 @@ const Index = ({ currentRound, transcoder, isActive }: Props) => {
248249
"0,0"
249250
)}`}
250251
/> */}
251-
<Stat
252-
className="masonry-grid_item"
253-
label="Fee Cut"
254-
tooltip={
255-
"The percent of the transcoding fees which are kept by the orchestrator, with the remainder distributed to its delegators by percent stake."
256-
}
257-
value={
258-
transcoder?.feeShare
259-
? numbro(1 - +(transcoder?.feeShare || 0) / 1000000).format({
260-
output: "percent",
261-
mantissa: 0,
262-
})
263-
: "N/A"
264-
}
265-
/>
266252
<Stat
267253
className="masonry-grid_item"
268254
label="Reward Cut"
@@ -280,6 +266,21 @@ const Index = ({ currentRound, transcoder, isActive }: Props) => {
280266
: "N/A"
281267
}
282268
/>
269+
<Stat
270+
className="masonry-grid_item"
271+
label="Fee Cut"
272+
tooltip={
273+
"The percent of the transcoding fees which are kept by the orchestrator, with the remainder distributed to its delegators by percent stake."
274+
}
275+
value={
276+
transcoder?.feeShare
277+
? numbro(1 - +(transcoder?.feeShare || 0) / 1000000).format({
278+
output: "percent",
279+
mantissa: 0,
280+
})
281+
: "N/A"
282+
}
283+
/>
283284
<Stat
284285
className="masonry-grid_item"
285286
label="Reward Calls"
@@ -329,112 +330,110 @@ const Index = ({ currentRound, transcoder, isActive }: Props) => {
329330
}
330331
/>
331332
)}
332-
<A
333-
as={Link}
334-
href={`/accounts/${transcoder?.id}/history`}
335-
passHref
336-
className="masonry-grid_item"
337-
css={{
338-
display: "block",
333+
</Masonry>
334+
<A
335+
as={Link}
336+
href={`/accounts/${transcoder?.id}/history`}
337+
passHref
338+
css={{
339+
display: "block",
340+
textDecoration: "none",
341+
marginBottom: "$3",
342+
"&:hover": {
339343
textDecoration: "none",
340-
"&:hover": {
341-
textDecoration: "none",
342-
".see-history": {
343-
textDecoration: "underline",
344-
color: "$primary11",
345-
transition: "color .3s",
346-
},
344+
".see-history": {
345+
textDecoration: "underline",
346+
color: "$primary11",
347+
transition: "color .3s",
347348
},
348-
}}
349-
>
350-
<Stat
351-
label="Treasury Governance Participation"
352-
variant="interactive"
353-
tooltip={
354-
<Box>
355-
Number of proposals voted on relative to the number of proposals
356-
the orchestrator was eligible for while active.
357-
</Box>
358-
}
359-
value={
360-
govStats ? (
361-
<Flex css={{ alignItems: "baseline", gap: "$1" }}>
362-
<Box css={{ color: "$hiContrast" }}>{govStats.voted}</Box>
349+
},
350+
}}
351+
>
352+
<Stat
353+
label="Treasury Governance Participation"
354+
variant="interactive"
355+
tooltip={
356+
<Box>
357+
Number of proposals voted on relative to the number of proposals
358+
the orchestrator was eligible for while active.
359+
</Box>
360+
}
361+
value={
362+
govStats ? (
363+
<Flex css={{ alignItems: "baseline", gap: "$1" }}>
364+
<Box css={{ color: "$hiContrast" }}>{govStats.voted}</Box>
365+
<Box
366+
css={{
367+
fontSize: "$3",
368+
color: "$neutral11",
369+
fontWeight: 500,
370+
}}
371+
>
372+
/ {govStats.eligible} Proposals
373+
</Box>
374+
</Flex>
375+
) : (
376+
"N/A"
377+
)
378+
}
379+
meta={
380+
<Box css={{ width: "100%", marginTop: "$2" }}>
381+
{govStats && (
382+
<Box
383+
css={{
384+
width: "100%",
385+
height: 4,
386+
backgroundColor: "$neutral4",
387+
borderRadius: "$2",
388+
overflow: "hidden",
389+
marginBottom: "$2",
390+
}}
391+
>
363392
<Box
364393
css={{
365-
fontSize: "$3",
366-
color: "$neutral11",
367-
fontWeight: 500,
394+
width: `${(govStats.voted / govStats.eligible) * 100}%`,
395+
height: "100%",
396+
backgroundColor: "$primary11",
368397
}}
369-
>
370-
/ {govStats.eligible} Proposals
371-
</Box>
372-
</Flex>
373-
) : (
374-
"N/A"
375-
)
376-
}
377-
meta={
378-
<Box css={{ width: "100%", marginTop: "$2" }}>
398+
/>
399+
</Box>
400+
)}
401+
<Flex
402+
css={{
403+
alignItems: "center",
404+
justifyContent: "space-between",
405+
width: "100%",
406+
}}
407+
>
379408
{govStats && (
380-
<Box
381-
css={{
382-
width: "100%",
383-
height: 4,
384-
backgroundColor: "$neutral4",
385-
borderRadius: "$2",
386-
overflow: "hidden",
387-
marginBottom: "$2",
388-
}}
389-
>
390-
<Box
391-
css={{
392-
width: `${(govStats.voted / govStats.eligible) * 100}%`,
393-
height: "100%",
394-
backgroundColor: "$primary11",
395-
}}
396-
/>
397-
</Box>
409+
<Text size="2" css={{ color: "$neutral11", fontWeight: 600 }}>
410+
{numbro(govStats.voted / govStats.eligible).format({
411+
output: "percent",
412+
mantissa: 0,
413+
})}{" "}
414+
Participation
415+
</Text>
398416
)}
399-
<Flex
417+
<Text
418+
className="see-history"
419+
size="2"
400420
css={{
421+
color: "$primary11",
422+
fontWeight: 600,
423+
display: "flex",
401424
alignItems: "center",
402-
justifyContent: "space-between",
403-
width: "100%",
425+
gap: "$0.75",
404426
}}
405427
>
406-
{govStats && (
407-
<Text
408-
size="2"
409-
css={{ color: "$neutral11", fontWeight: 600 }}
410-
>
411-
{numbro(govStats.voted / govStats.eligible).format({
412-
output: "percent",
413-
mantissa: 0,
414-
})}{" "}
415-
Participation
416-
</Text>
417-
)}
418-
<Text
419-
className="see-history"
420-
size="2"
421-
css={{
422-
color: "$primary11",
423-
fontWeight: 600,
424-
display: "flex",
425-
alignItems: "center",
426-
gap: "$0.75",
427-
}}
428-
>
429-
See history
430-
<Box as={ArrowTopRightIcon} width={15} height={15} />
431-
</Text>
432-
</Flex>
433-
</Box>
434-
}
435-
/>
436-
</A>
437-
</Masonry>
428+
See history
429+
<Box as={ArrowTopRightIcon} width={15} height={15} />
430+
</Text>
431+
</Flex>
432+
</Box>
433+
}
434+
/>
435+
</A>
436+
<OrchestratorCutHistory transcoder={transcoder} />
438437
</Box>
439438
);
440439
};

0 commit comments

Comments
 (0)