Skip to content

fix: manually surface LIP-118 poll while subgraph indexing is behind - #734

Merged
rickstaa merged 1 commit into
mainfrom
fix/manually-list-lip118-poll
Jul 20, 2026
Merged

fix: manually surface LIP-118 poll while subgraph indexing is behind#734
rickstaa merged 1 commit into
mainfrom
fix/manually-list-lip118-poll

Conversation

@rickstaa

@rickstaa rickstaa commented Jul 20, 2026

Copy link
Copy Markdown
Member

Summary

A governance poll created while the subgraph was behind on indexing — LIP-118 "Delegated Reward Calling" (creation tx), doesn't show up on the voting pages or in the Governance nav badge, because those are sourced entirely from the subgraph. Voting itself is an on-chain action, but the poll was unreachable through the UI.

This adds a temporary manual entry so the poll is listed and votable during the outage. It self-cleans once the subgraph reindexes the poll.

What changed

  • constants/manualPolls.ts (new) — MANUAL_POLLS (subgraph-shaped entries) seeded with LIP-118, plus mergeManualPolls / getManualPoll / isManualPoll helpers. Merges are deduped by poll address, so the real subgraph copy wins once indexed and the manual entry drops out automatically.
  • pages/voting/index.tsx — merge manual polls into the list; render as soon as the on-chain current round is available (so the list shows even if the subgraph query returns nothing).
  • pages/voting/[poll].tsx — fall back to the manual entry on the detail page; don't 404 when a manual entry exists; show a blue "counts not loaded" note above the tally for manual polls.
  • layouts/main.tsx — include manual polls in the active-poll count so the green Governance badge reflects the live LIP.
  • lib/api/polls.tsgetTotalStake degrades gracefully (returns undefined) if the subgraph is unavailable, so a manual poll still renders during a full outage.

Behavior

  • Casting votes works normally — it's on-chain (Poll.vote), and the eligibility gate reads on-chain pending stake, not the subgraph.
  • Live tallies are not reconstructed — manual polls carry no tally, so vote counts read 0 until indexing catches up (hence the note). Cast votes are recorded on-chain and backfill automatically on recovery.

Reverting

Delete constants/manualPolls.ts and its imports (or just empty the MANUAL_POLLS array) once the subgraph has reindexed the poll.

Testing

  • pnpm typecheck + pnpm lint pass.
  • Pre-commit prettier/eslint hooks pass.

Summary by CodeRabbit

  • New Features

    • Added fallback support for manually configured governance polls when indexing is unavailable.
    • Added a notice on manually recovered polls indicating that live vote counts may be delayed.
    • Active governance poll counts now include available manual poll entries.
  • Bug Fixes

    • Voting pages can load when poll indexing data is temporarily unavailable.
    • Poll rendering now handles active stake query failures without breaking the page.

The voting list/detail pages and the Governance nav badge source polls
entirely from the subgraph, so a poll created during an indexing halt
(LIP-118 "Delegated Reward Calling") doesn't appear and can't be reached
for voting through the UI — even though voting itself is an on-chain action.

Add a temporary MANUAL_POLLS constant (subgraph-shaped entries) merged into
the voting list, the poll detail page, and the active-poll nav count, deduped
by poll address so the real tallied copy wins and the manual entry drops out
once indexing recovers. Manual polls carry no tally, so the detail page shows
a "counts not loaded" note; casting votes works normally on-chain. getTotalStake
now degrades gracefully if the subgraph is unavailable so manual polls still
render.

Revert once the subgraph has reindexed the poll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explorer-arbitrum-one Ready Ready Preview, Comment Jul 20, 2026 11:11pm

Request Review

@rickstaa
rickstaa merged commit 29fd3cc into main Jul 20, 2026
9 of 10 checks passed
@rickstaa
rickstaa deleted the fix/manually-list-lip118-poll branch July 20, 2026 23:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a temporary “manual poll” fallback so a governance poll (LIP-118) remains discoverable and votable in the UI when the subgraph is behind or unavailable, and self-cleans once indexing catches up.

Changes:

  • Introduces constants/manualPolls.ts with a manually-seeded poll list and helpers to merge/dedupe against subgraph results.
  • Updates voting list/detail pages to fall back to manual entries when the subgraph doesn’t return the poll.
  • Makes the Governance badge count include manual polls, and makes total stake fetching degrade gracefully during subgraph outages.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
constants/manualPolls.ts Adds manual poll entries plus merge/lookup helpers for subgraph-shaped polls.
pages/voting/index.tsx Merges manual polls into the voting list, rendering once on-chain round data is available.
pages/voting/[poll].tsx Adds manual fallback on poll detail, avoids 404 for manual polls, and displays an indexing/tally note.
layouts/main.tsx Includes manual polls in the active-poll badge count.
lib/api/polls.ts Wraps total stake subgraph query in try/catch to allow rendering during outages.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread constants/manualPolls.ts
@@ -0,0 +1,58 @@
import { PollsQuery } from "apollo";
Comment thread pages/voting/[poll].tsx
Comment on lines +108 to 111
// Only 404 on a genuine subgraph error with no manual fallback for this poll.
if (pollError && !getManualPoll(pollId)) {
return <FourZeroFour />;
}
Comment thread lib/api/polls.ts
Comment on lines +192 to +199
} catch (err) {
// The subgraph can be unavailable (e.g. an indexing halt). Total stake only
// feeds participation percentages, so degrade gracefully instead of failing
// the whole poll render — this keeps manually-listed polls viewable/votable.
const detail = err instanceof Error ? err.message : String(err);
console.warn(`Could not fetch total stake from subgraph (${detail})`);
return undefined;
}
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds manually configured governance poll fallbacks, integrates them into voting views and active-poll counts, and makes total active stake retrieval return undefined when the subgraph query fails.

Changes

Manual poll fallback

Layer / File(s) Summary
Manual poll data and merge helpers
constants/manualPolls.ts
Defines a subgraph-shaped manual poll, case-insensitive lookup helpers, and deduplicating merge behavior.
Voting list and detail integration
pages/voting/index.tsx, pages/voting/[poll].tsx
Uses merged polls in the list, falls back to manual polls on the detail page, and displays a manual-poll warning.
Active counts and stake-query resilience
layouts/main.tsx, lib/api/polls.ts
Includes manual polls in active counts and handles total-stake query failures with a warning and undefined result.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VotingIndex
  participant ManualPollHelpers
  participant PollData
  participant GetPollExtended
  VotingIndex->>PollData: read subgraph polls
  VotingIndex->>ManualPollHelpers: mergeManualPolls(data?.polls)
  ManualPollHelpers-->>VotingIndex: merged poll list
  VotingIndex->>GetPollExtended: load polls at current L1 block
  GetPollExtended-->>VotingIndex: extended poll data
Loading

Suggested reviewers: ecwireless, copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is detailed and relevant, but it omits the template's required Impact / Risk section and related issue/type metadata. Add Impact / Risk details and fill in Type of Change and Related Issue(s) to match the repository template.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main change: surfacing a manual LIP-118 poll while subgraph indexing lags.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/manually-list-lip118-poll

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pages/voting/index.tsx (1)

53-71: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fix async useEffect race conditions.

Problem: The async init() calls inside these effects do not discard stale responses. When data updates (e.g., from undefined to populated), a second init() triggers. If the network requests for the newer execution complete before the older one, the older fallback-driven response will erroneously overwrite the final state.
Why it matters: Network latency variations can cause the page to revert to displaying only the manual fallback data, hiding real active polls even when the subgraph is fully operational.
Suggested fix: Implement a local ignore flag to discard stale closures.

  • pages/voting/index.tsx#L53-L71: Set an let ignore = false; variable, only update state if (!ignore), and return () => { ignore = true; }.
  • pages/voting/[poll].tsx#L92-L106: Apply the same ignore flag pattern to prevent stale pollData state overwrites.

As per path instructions, ensure correctness and realistic production edge cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/voting/index.tsx` around lines 53 - 71, The asynchronous initialization
in the useEffect at pages/voting/index.tsx:53-71 can overwrite newer results
with stale responses; add a local ignore flag, guard setPolls and setLoading
with it, and return cleanup that marks the effect stale. Apply the same
ignore-flag cleanup and guarded state update to the pollData effect at
pages/voting/[poll].tsx:92-106.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/api/polls.ts`:
- Around line 196-199: Update getPollExtended to guard both
totalParticipationPercent and nonVotersPercent calculations against totalStake
being zero or otherwise non-positive, returning 0 instead of performing division
in that fallback case. Preserve the existing percentage calculations when
totalStake is greater than zero.

In `@pages/voting/`[poll].tsx:
- Around line 220-236: The manual-poll fallback warning currently renders
whenever the poll ID is in the hardcoded list, even when indexed subgraph data
is available. Update the condition around the warning Box to require both an
absent data?.poll result and isManualPoll(pollData.id), preserving the warning
only while fallback data is actively used.

---

Outside diff comments:
In `@pages/voting/index.tsx`:
- Around line 53-71: The asynchronous initialization in the useEffect at
pages/voting/index.tsx:53-71 can overwrite newer results with stale responses;
add a local ignore flag, guard setPolls and setLoading with it, and return
cleanup that marks the effect stale. Apply the same ignore-flag cleanup and
guarded state update to the pollData effect at pages/voting/[poll].tsx:92-106.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ebec1646-2287-4c3a-8a2f-0edb91537d2d

📥 Commits

Reviewing files that changed from the base of the PR and between 0882289 and dd2c88e.

📒 Files selected for processing (5)
  • constants/manualPolls.ts
  • layouts/main.tsx
  • lib/api/polls.ts
  • pages/voting/[poll].tsx
  • pages/voting/index.tsx

Comment thread lib/api/polls.ts
Comment on lines +196 to +199
const detail = err instanceof Error ? err.message : String(err);
console.warn(`Could not fetch total stake from subgraph (${detail})`);
return undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent downstream division by zero when the fallback triggers.

Problem: Returning undefined here causes totalStake to evaluate to 0 in getPollExtended. This results in division by zero (0/0 or votes/0), generating NaN or Infinity for the participation percentages.
Why it matters: The graceful degradation introduces visual bugs (e.g., NaN%) instead of a clean fallback.
Suggested fix: Ensure getPollExtended safeguards against totalStake === 0 when calculating totalParticipationPercent and nonVotersPercent (e.g., totalStake > 0 ? totalVoteStake / totalStake : 0).

As per path instructions, check for correctness and runtime issues.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/api/polls.ts` around lines 196 - 199, Update getPollExtended to guard
both totalParticipationPercent and nonVotersPercent calculations against
totalStake being zero or otherwise non-positive, returning 0 instead of
performing division in that fallback case. Preserve the existing percentage
calculations when totalStake is greater than zero.

Source: Path instructions

Comment thread pages/voting/[poll].tsx
Comment on lines +220 to +236
{isManualPoll(pollData.id) && (
<Box
css={{
marginBottom: "$3",
padding: "$3",
borderRadius: "$3",
border: "1px solid $blue5",
backgroundColor: "$blue2",
}}
>
<Text size="1" css={{ color: "$blue11" }}>
Live vote counts aren&apos;t available for this poll yet —
the subgraph is still indexing it. The totals below may read
0 until indexing catches up. Voting works normally.
</Text>
</Box>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Ensure the fallback warning only displays when the fallback is actively used.

Problem: isManualPoll(pollData.id) evaluates to true if the ID exists in the hardcoded list, even after data?.poll successfully loads and replaces the fallback with real indexed subgraph data.
Why it matters: Users will see real vote totals alongside a confusing warning stating that vote counts are not yet available.
Suggested fix: Update the condition to check that the subgraph query actually failed to provide the poll, for example: {!data?.poll && isManualPoll(pollData.id)}.

As per path instructions, focus on correctness and real bugs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/voting/`[poll].tsx around lines 220 - 236, The manual-poll fallback
warning currently renders whenever the poll ID is in the hardcoded list, even
when indexed subgraph data is available. Update the condition around the warning
Box to require both an absent data?.poll result and isManualPoll(pollData.id),
preserving the warning only while fallback data is actively used.

Source: Path instructions

rickstaa added a commit that referenced this pull request Jul 21, 2026
…h resyncs (#749)

Stopgap while the subgraph resyncs from scratch (livepeer/subgraph#247), which
leaves the manually surfaced LIP-118 poll showing 0% support and 0% participation
even for people who already voted.

Revert this and #734 once the subgraph has reindexed the poll.
---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <copilot-pull-request-reviewer[bot]@users.noreply.github.com>
moudi-network pushed a commit to moudi-network/explorer that referenced this pull request Jul 24, 2026
…ivepeer#734)

The voting list/detail pages and the Governance nav badge source polls
entirely from the subgraph, so a poll created during an indexing halt
(LIP-118 "Delegated Reward Calling") doesn't appear and can't be reached
for voting through the UI — even though voting itself is an on-chain action.

Add a temporary MANUAL_POLLS constant (subgraph-shaped entries) merged into
the voting list, the poll detail page, and the active-poll nav count, deduped
by poll address so the real tallied copy wins and the manual entry drops out
once indexing recovers. Manual polls carry no tally, so the detail page shows
a "counts not loaded" note; casting votes works normally on-chain. getTotalStake
now degrades gracefully if the subgraph is unavailable so manual polls still
render.

Revert once the subgraph has reindexed the poll.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
moudi-network pushed a commit to moudi-network/explorer that referenced this pull request Jul 24, 2026
…h resyncs (livepeer#749)

Stopgap while the subgraph resyncs from scratch (livepeer/subgraph#247), which
leaves the manually surfaced LIP-118 poll showing 0% support and 0% participation
even for people who already voted.

Revert this and livepeer#734 once the subgraph has reindexed the poll.
---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <copilot-pull-request-reviewer[bot]@users.noreply.github.com>
moudi-network pushed a commit to moudi-network/explorer that referenced this pull request Jul 24, 2026
…ivepeer#734)

The voting list/detail pages and the Governance nav badge source polls
entirely from the subgraph, so a poll created during an indexing halt
(LIP-118 "Delegated Reward Calling") doesn't appear and can't be reached
for voting through the UI — even though voting itself is an on-chain action.

Add a temporary MANUAL_POLLS constant (subgraph-shaped entries) merged into
the voting list, the poll detail page, and the active-poll nav count, deduped
by poll address so the real tallied copy wins and the manual entry drops out
once indexing recovers. Manual polls carry no tally, so the detail page shows
a "counts not loaded" note; casting votes works normally on-chain. getTotalStake
now degrades gracefully if the subgraph is unavailable so manual polls still
render.

Revert once the subgraph has reindexed the poll.
moudi-network pushed a commit to moudi-network/explorer that referenced this pull request Jul 24, 2026
…h resyncs (livepeer#749)

Stopgap while the subgraph resyncs from scratch (livepeer/subgraph#247), which
leaves the manually surfaced LIP-118 poll showing 0% support and 0% participation
even for people who already voted.

Revert this and livepeer#734 once the subgraph has reindexed the poll.
---------

Co-authored-by: Copilot <copilot-pull-request-reviewer[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants