Skip to content

feat: add delegators view#719

Open
moudi-network wants to merge 6 commits into
livepeer:mainfrom
moudi-network:feat/711/delegators-view
Open

feat: add delegators view#719
moudi-network wants to merge 6 commits into
livepeer:mainfrom
moudi-network:feat/711/delegators-view

Conversation

@moudi-network

@moudi-network moudi-network commented Jun 30, 2026

Copy link
Copy Markdown

Description

Adds a Delegators tab to the orchestrator account page, listing every account that has delegated stake to the orchestrator. Each row shows the delegator's Stake (subgraph bondedAmount) and Pending Rewards (live on-chain pendingStake − bondedAmount), so a row's total matches what each delegator sees on their own profile.

Built entirely on existing infrastructure: the subgraph Transcoder.delegators field (no subgraph/indexer change) and the existing /api/pending-stake endpoint (no new server code). Pending Rewards is fetched per visible row via the existing usePendingFeesAndStakeData hook — the same per-cell pattern already used for ENS data. Only the current page (~10 rows) are fetched.

Type of Change

  • feat: New feature

Related Issue(s)

Related: #711
Closes: #106

Changes Made

Delegators List

  • Add a Delegators tab to the account page, gated to orchestrators (isOrchestrator || isMyDelegate), positioned after Delegating
  • New queries/orchestratorDelegators.graphql querying the existing Transcoder.delegators field (regenerated Apollo types via pnpm codegen)
  • New components/DelegatorList (react-table: columns, custom numericSort, client pagination, empty state) and components/DelegatorsView wrapper
  • New SSR route pages/accounts/[account]/delegators.tsx (mirrors orchestrating.tsx)
  • Stake column from subgraph bondedAmount; Pending Rewards column computed live per row as max(0, pendingStake/1e18 − bondedAmount) via /api/pending-stake

Testing

  • Tested locally
  • Added/updated tests
  • All tests passing

How to test (optional unless test is not trivial)

Delegators List

  • Open an orchestrator account → Delegators tab; confirm the tab is present on orchestrators and absent on plain delegator accounts.
  • Pick a delegator row and open that delegator's own profile: the row's Stake + Pending Rewards should equal the profile's "Stake". Verified on 0x1416… orchestrator → delegator 0xa69e…3db3: 131.43K + 28.72K = 160.15K (matches profile).
  • pnpm codegen, pnpm typecheck, pnpm exec eslint … --max-warnings 0, prettier all clean.

Impact / Risk

Risk level: Low

Impacted areas: UI (new tab/route + right-rail copy), read-only API reuse (/api/pending-stake). No DB, infra, config, or new server code.

User impact: Orchestrator account pages gain a new Delegators tab. No change for plain delegator accounts. Purely additive. Nothing existing is altered.

Rollback plan: Plain PR revert. The change is additive (new route + tab branch + new components), so reverting removes the tab with no migration or config cleanup.

Screenshots / Recordings (if applicable)

Screenshot 2026-06-28 at 00 55 49

Additional Notes

  • Pending Rewards is fetched per visible row (reusing /api/pending-stake) to keep the PR minimal. No new API route/hook/type. Trade-off: the column is not globally sortable (the value only exists for on-screen rows). A batched-endpoint alternative (sortable column, 1 request) can be implemented if wanted later.
  • No "Fees" column: subgraph Delegator.fees is lifetime-cumulative across all orchestrators and event-lagged, so it isn't attributable to this orchestrator.
  • Scope: loads up to first: 1000 delegators sorted by stake, paginated client-side (10/page); fine for current orchestrator sizes.

Summary by CodeRabbit

  • New Features
    • Added a Delegators tab and a dedicated page to view a transcoder’s delegators.
    • Includes a sortable delegators table (stake, pending rewards, start round) with quick Profile/History actions and automatic multi-page loading.
  • Bug Fixes
    • Improved account layout so wide tables don’t constrain the central column.
    • Added resilient pagination behavior with an inline warning and retry if later pages fail.
  • Style
    • Enhanced the shared table component with an optional minimum width setting.

Adds a Delegators tab to the account layout listing an orchestrator's
delegators, backed by a new orchestratorDelegators subgraph query.
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@moudi-network is attempting to deploy a commit to the Livepeer Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a delegator list to the orchestrator account flow and a new logs query surface. The change includes generated Apollo types and hooks, a paginated delegator table, account layout wiring, and a dedicated delegators page.

Changes

Indexing logs query

Layer / File(s) Summary
Log query types
apollo/subgraph.ts
Adds the LogLevel enum, log data types, the Query._logs field, and the _logs argument type.

Orchestrator delegators list

Layer / File(s) Summary
GraphQL query and generated Apollo types
queries/orchestratorDelegators.graphql, apollo/subgraph.ts
Adds the orchestratorDelegators query and the generated TypeScript types, document, and hooks for it.
DelegatorList table component
components/DelegatorList/index.tsx, components/Table/index.tsx
Renders delegator rows with identity, stake, pending rewards, start round, and actions; adds the empty state and the shared minWidth prop used by the table.
DelegatorsView wrapper component
components/DelegatorsView/index.tsx
Runs the delegators query, pages through delegator results, and renders DelegatorList with loading and error states.
Account layout tab and view wiring
layouts/account.tsx
Adds the delegators tab/view and renders DelegatorsView inside the account layout.
Delegators page route
pages/accounts/[account]/delegators.tsx
Adds the /accounts/[account]/delegators page with static path generation, static props fetching, and account layout rendering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AccountLayout
  participant DelegatorsView
  participant useOrchestratorDelegatorsQuery
  participant Subgraph
  participant DelegatorList

  AccountLayout->>DelegatorsView: render delegators view
  DelegatorsView->>useOrchestratorDelegatorsQuery: query transcoder id + pagination
  useOrchestratorDelegatorsQuery->>Subgraph: orchestratorDelegators(...)
  Subgraph-->>useOrchestratorDelegatorsQuery: transcoder.delegators
  useOrchestratorDelegatorsQuery-->>DelegatorsView: data and fetchMore results
  DelegatorsView->>DelegatorList: delegators array
  DelegatorList-->>AccountLayout: table or empty state
Loading

Related issue: #106
Suggested reviewers: ECWireless

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a delegators view/tab.
Description check ✅ Passed The PR description follows the template and includes the required sections with clear, specific content.
Linked Issues check ✅ Passed The changes satisfy #106 by adding a Delegators tab and list on the orchestrator detail view.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes stand out; the added query, components, route, and table tweak support the delegators feature.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@moudi-network

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 3

🤖 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 `@components/DelegatorsView/index.tsx`:
- Around line 15-23: The delegators list is being silently truncated by the
hard-coded `first: 1000` in `useOrchestratorDelegatorsQuery`, while the table
only paginates the fetched data client-side. Update `DelegatorsView` to drive
pagination from table state and fetch additional rows from the server using
`first`/`skip` (or cursors) instead of a fixed limit. Use the existing
`useOrchestratorDelegatorsQuery` setup and the table’s pagination controls to
wire the requested page size and offset through the query variables.
- Around line 15-27: The `DelegatorsView` render path is treating both in-flight
and failed `useOrchestratorDelegatorsQuery` results as an empty list, so
`DelegatorList` shows a false “No delegators found” state. Update
`DelegatorsView` to read `loading` and `error` from
`useOrchestratorDelegatorsQuery`, render a loading or error state before
`DelegatorList`, and only pass through to the empty-state logic after a
successful query with zero delegators.

In `@pages/accounts/`[account]/delegators.tsx:
- Around line 59-88: The delegators page currently allows any valid account
through `getStaticProps`, but this route should only serve orchestrator
accounts. In `getStaticProps`, after `getAccount(client, accountId)` and before
fetching orchestrators, check `account.data.transcoder`; if it is missing,
return `notFound: true` or redirect to `/delegating` instead of building
`PageProps`. Keep the existing `getAccount`, `getSortedOrchestrators`, and
`PageProps` flow only for accounts that have a transcoder.
🪄 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: 70cf5959-3344-4ea1-be5d-effb16836fa5

📥 Commits

Reviewing files that changed from the base of the PR and between 48e5844 and 34f87fc.

📒 Files selected for processing (7)
  • apollo/subgraph.ts
  • components/DelegatorList/index.tsx
  • components/DelegatorsView/index.tsx
  • components/Table/index.tsx
  • layouts/account.tsx
  • pages/accounts/[account]/delegators.tsx
  • queries/orchestratorDelegators.graphql

Comment thread components/DelegatorsView/index.tsx Outdated
Comment thread components/DelegatorsView/index.tsx Outdated
Comment thread pages/accounts/[account]/delegators.tsx

@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

🤖 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 `@components/DelegatorsView/index.tsx`:
- Around line 38-69: The `fetchNext` pagination path in `DelegatorsView` only
logs `fetchMore` failures, which can leave the list silently incomplete after a
later page load fails. Update `fetchNext` so it sets a visible failure state
(for example `pageFetchFailed`) when `fetchMore` throws, and keep the state
reset in `finally` so retries remain possible. Then use that state in
`DelegatorsView` to render a small banner/notice near the table, and optionally
provide a retry action, so users know paging stopped early.
- Around line 93-94: The DelegatorsView render path is performing a side effect
by calling console.error(error) directly inside the component body when error &&
!delegators is true. Move this logging into a useEffect in DelegatorsView keyed
on error (and any other needed state) so the log only runs after render and
avoids duplicate or discarded render side effects.
🪄 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: f80ae7c1-d6b1-4b3f-9281-a72a2621c274

📥 Commits

Reviewing files that changed from the base of the PR and between 34f87fc and 5ed5b2d.

📒 Files selected for processing (2)
  • components/DelegatorsView/index.tsx
  • pages/accounts/[account]/delegators.tsx

Comment thread components/DelegatorsView/index.tsx
Comment thread components/DelegatorsView/index.tsx Outdated

@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.

Caution

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

⚠️ Outside diff range comments (2)
components/DelegatorsView/index.tsx (2)

21-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a brief description for this non-trivial component.

DelegatorsView implements query wiring, cursor-less pagination, retry/failure handling, and multiple render states, but has no descriptive comment explaining its behavior/contract for future maintainers.

As per path instructions, "at minimum, include a general description for new functions/components that are not self-explanatory."

🤖 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 `@components/DelegatorsView/index.tsx` around lines 21 - 33, DelegatorsView is
a non-trivial component and needs a brief descriptive comment explaining its
purpose and contract for maintainers. Add a short component-level description
near the DelegatorsView definition that summarizes its query wiring via
useOrchestratorDelegatorsQuery, pagination/fetchMore behavior, and the
loading/error/empty/content render states so the intent is clear without reading
the full implementation.

Source: Path instructions


42-85: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Stop auto-paging once the last page is shorter than PAGE_SIZE.
delegators.length % PAGE_SIZE === 0 treats an exact multiple as “more data,” so the final empty page can keep re-triggering fetchNext with the same skip and spam the subgraph. Track a hasMore flag from the last response instead of inferring it from the current length.

🤖 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 `@components/DelegatorsView/index.tsx` around lines 42 - 85, The auto-paging
logic in fetchNext/useEffect is inferring pagination from delegators.length %
PAGE_SIZE, which can keep requesting the same skip value after the last full
page. Update the DelegatorsView paging flow to track an explicit hasMore flag
from the latest fetchMore response and use that in the useEffect guard instead
of relying on the current list length; make sure fetchNext clears hasMore when
fetchMoreResult.transcoder.delegators is empty or shorter than PAGE_SIZE.
🤖 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.

Outside diff comments:
In `@components/DelegatorsView/index.tsx`:
- Around line 21-33: DelegatorsView is a non-trivial component and needs a brief
descriptive comment explaining its purpose and contract for maintainers. Add a
short component-level description near the DelegatorsView definition that
summarizes its query wiring via useOrchestratorDelegatorsQuery,
pagination/fetchMore behavior, and the loading/error/empty/content render states
so the intent is clear without reading the full implementation.
- Around line 42-85: The auto-paging logic in fetchNext/useEffect is inferring
pagination from delegators.length % PAGE_SIZE, which can keep requesting the
same skip value after the last full page. Update the DelegatorsView paging flow
to track an explicit hasMore flag from the latest fetchMore response and use
that in the useEffect guard instead of relying on the current list length; make
sure fetchNext clears hasMore when fetchMoreResult.transcoder.delegators is
empty or shorter than PAGE_SIZE.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b22ee49f-1b03-4b4a-86f3-a6bf8a034fd7

📥 Commits

Reviewing files that changed from the base of the PR and between 5ed5b2d and 77a7714.

📒 Files selected for processing (1)
  • components/DelegatorsView/index.tsx

@vercel

vercel Bot commented Jul 6, 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 6, 2026 2:52pm

Request Review

@rickstaa

rickstaa commented Jul 7, 2026

Copy link
Copy Markdown
Member

@moudi-network thanks for the contribution.

@mehrdadmms did an initial review, and everything looks good so far. The implementation works, and the code quality is high enough for the bounty to be paid out. I may request a few minor code and UI changes after Adam has reviewed it. I will try to merge this in in the comming weeks when I have more bandwith.

key1989han added a commit to key1989han/explorer that referenced this pull request Jul 17, 2026
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.

Add a delegator list to the orchestrator detail view

2 participants