fix: show delegated reward calls in account history - #757
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe transaction query now returns delegated reward events and supports contract-creation transactions. ChangesReward history integration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant HistoryView
participant TransactionsDocument
participant Subgraph
participant HistoryList
HistoryView->>TransactionsDocument: request account history page
TransactionsDocument->>Subgraph: query transactions, winning tickets, and rewardEvents
Subgraph-->>TransactionsDocument: return account-matching event data
TransactionsDocument-->>HistoryView: provide TransactionsQuery results
HistoryView->>HistoryView: merge events and remove duplicate self-called rewards
HistoryView->>HistoryList: render merged history
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/HistoryView/index.tsx`:
- Around line 178-180: Update the rewardEvents filter in HistoryView to use an
inclusive timestamp boundary so events with timestamps equal to
lastEventTimestamp are retained. Preserve the existing fallback behavior, and
add a regression test covering reward and transaction events sharing the
boundary timestamp.
🪄 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 Plus
Run ID: 764b3a83-99fe-44ce-a1c7-3c7df91deefe
📒 Files selected for processing (3)
apollo/subgraph.tscomponents/HistoryView/index.tsxqueries/transactions.graphql
There was a problem hiding this comment.
Pull request overview
Fixes the account History view for orchestrators under LIP-118 by ensuring delegated reward() calls (submitted by a reward caller, not the orchestrator) still appear in the orchestrator’s timeline. This is done by querying RewardEvent entities directly by delegate and merging them into the history feed while avoiding duplicates.
Changes:
- Add a delegate-keyed
rewardEvents(where: { delegate: $account })query alongside the existing sender-keyedtransactionsquery. - Merge
rewardEventsintoHistoryView(and exclude tx-pathRewardEvents) to ensure rewards render exactly once. - Update paging/end-of-list logic to require all parallel lists to be exhausted before stopping.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| queries/transactions.graphql | Adds top-level rewardEvents query keyed by RewardEvent.delegate to capture delegated reward calls. |
| components/HistoryView/index.tsx | Merges rewardEvents into the timeline and updates paging/end-of-list handling and empty state conditions. |
| apollo/subgraph.ts | Regenerates GraphQL types/documents to include the new rewardEvents field and updated schema typing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Reviewable in 30 seconds — real A/B on the same accountThe first delegated reward call now exists on Arbitrum, so this can be verified live against the Look for Round #4,290 —
That transaction is While you are on the preview, the dedupe is worth a glance too: the self-called rewards for the preceding rounds each appear exactly once. That is the regression risk in this PR, since rewards now arrive via a second query and there is no id-based dedupe in the component. |
Account history is keyed on the transaction sender:
transactions(where: { from: $account })
Since LIP-118 (livepeer/protocol#648) an orchestrator can nominate a
reward caller to submit reward() on its behalf. That transaction is sent
by the caller, so it drops out of the query and every event inside it -
including the RewardEvent - disappears from the orchestrator's history.
The event itself was always attributed correctly: RewardEvent.delegate is
the orchestrator regardless of who signed. The blind spot is purely in how
the Explorer asks for it. It cannot be fixed in place either, because
Transaction.events is typed as the Event interface, whose filter exposes
only id/timestamp/transaction/round - delegate lives on the concrete
RewardEvent type, so events_: { delegate: ... } does not exist.
So query the event entity directly, keyed on the orchestrator. This is the
same shape as winningTicketRedeemedEvents, already a separate role-keyed
query in this file for the same reason: a ticket's recipient is an event
param, so it survives being redeemed by a separate wallet.
Excluding RewardEvent from the transaction-path list is what keeps
self-called rewards, which appear in both queries, from rendering twice -
there is no id-based dedupe in this component.
Also require every list to be exhausted before paging stops. Keying that
on transactions alone cuts off exactly the accounts this fixes: an
orchestrator delegating every reward call has few transactions but one
reward event per round. This equally affects gateways with more tickets
than transactions, which 90ab4a1 fixed for totalLoaded but not reachedEnd.
Delegated calls render with the existing copy. Distinguishing them would
mean comparing transaction.from against the account, which is approximate
- a multisig or relayer shows as the sender. The authoritative answer is
Transcoder.rewardCaller, pending livepeer/subgraph#253.
a2f15b9 to
73c0d5b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
components/HistoryView/index.tsx:181
- The reward-events window filter uses a strict
>comparison againstlastEventTimestamp. Because many events share the same block timestamp, this can permanently exclude delegated RewardEvents whosetransaction.timestampequals the oldest loaded transaction timestamp (especially when there are no older transactions to load, so the boundary never moves). Using an inclusive boundary avoids stranding same-timestamp events at the paging edge.
data?.rewardEvents?.filter(
(e) => (e?.transaction?.timestamp ?? 0) > lastEventTimestamp
) ?? [],
Follow-up to #757. Render RewardCallerSetEvent on the home and transactions pages and on the account history, including the unset variant (zero address). Shown for any address: setRewardCaller has no registration check, and filtering on current transcoder status would rewrite a historical event. Also widen the reward query from #757, which was keyed on the delegate alone and left a nominated caller's own history without the calls it made. It now matches the sender too; those rows read "Called reward for <orchestrator>" with no LPT amount, which is minted for the orchestrator. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
Since LIP-118 (livepeer/protocol#648, already live on Arbitrum) an orchestrator can call
setRewardCallerto nominate an address that then callsrewardForTranscoderon its behalf.Account history queries
transactions(where: { from: $account })— keyed on the transaction sender. A delegated reward is sent by the caller, so the whole transaction, and theRewardEventinside it, vanishes from the orchestrator's history.This adds a second query keyed on the orchestrator (
rewardEvents(where: { delegate: $account })) and merges it into the timeline, mirroring howwinningTicketRedeemedEventsis already handled.Finding 2 of the LIP-118 audit. No subgraph changes needed —
RewardEvent.delegatehas always been the orchestrator.Type of Change
Related Issue(s)
Related: livepeer/subgraph#253
Changes Made
queries/transactions.graphql— new top-levelrewardEvents(where: { delegate: $account })components/HistoryView/index.tsx— merge the list in: window filter,mergedEvents,totalLoaded,updateQuery, empty stateRewardEventfrom the transaction-path list — this is the dedupe. Self-called rewardsarrive via both queries and there is no id-based dedupe in this component
reachedEndnow requires all lists exhausted, not justtransactions— an orchestrator delegatingevery reward has few transactions but one reward per round, so paging stopped after one page
apollo/subgraph.ts— regeneratedTesting
pnpm lint,pnpm test(363),pnpm build,tsc --noEmitclean.Verified against the first real delegated reward call on Arbitrum (
0xc5377ebd…): it appears in the orchestrator's history on the preview and is absent on production. See the A/B links in the comment below.Impact / Risk
Risk level: Low — UI only, account history view.
User impact: delegated reward calls appear in the orchestrator's history instead of vanishing.
Trade-off: they no longer appear in the reward caller's history, which is correct — the caller
receives nothing.
Rollback plan: PR revert. No schema, migration or config change.
Additional Notes
transaction.from, which is approximate (a multisig shows as the sender). The authoritative fieldis
Transcoder.rewardCallerfrom feat: index RewardCallerSet reward caller delegation subgraph#253, not yet deployed — follow-up once it is.TransactionsListneeds no change; it already renders fromevent.delegate.id.